library(readxl)
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.3.6      ✔ purrr   0.3.5 
## ✔ tibble  3.1.8      ✔ dplyr   1.0.10
## ✔ tidyr   1.2.1      ✔ stringr 1.4.1 
## ✔ readr   2.1.3      ✔ forcats 0.5.2 
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()

PTM LEVEL

load("MSstats_models/adjusted_models_sim1.rda")
load("MSstats_models/adjusted_models_sim2.rda")
sign_table11 <- readRDS("simulation 1/sign_table11.rds")
sign_table12 <- readRDS("simulation 1/sign_table12.rds")
sign_table13 <- readRDS("simulation 1/sign_table13.rds")

sign_table21 <- readRDS("simulation 2/sign_table21.rds")
sign_table22 <- readRDS("simulation 2/sign_table22.rds")
sign_table23 <- readRDS("simulation 2/sign_table23.rds")
msqrob_simulation1 <- rbind(sign_table11, sign_table12, sign_table13)
msqrob_simulation2 <- rbind(sign_table21, sign_table22, sign_table23)
s <- c(.2,.3)
reps <- c(2,3,5,10)
cond <- c(2,3,4)
param_combos <- expand.grid(s, reps, cond)

0.1 msstats type visualisations

Some of the functions below were found in the simulation analysis files on Massive from the MSstats team

0.2 sensitivy - fdp plots

0.2.1 MSstats

Function to calculate TPR and FDP

tprFdp <- function(df, pval, tp){
  ord <- order(df[[pval]])
  df <- df[ord,]
  df$tpr = as.numeric(cumsum(df[[tp]])/sum(df[[tp]]))
  df$fdp = as.numeric(cumsum(!df[[tp]])/(1:length(df[[tp]])))
  df$fpr = as.numeric(cumsum(!df[[tp]])/sum(!df[[tp]]))
  df
}

0.2.1.1 Simulation 1

for (i in 1:length(adjusted_models_sim1)){
  adjusted_models_sim1[[i]] <- adjusted_models_sim1[[i]] %>%
                                mutate(sd = param_combos[i,1], reps = param_combos[i,2],
                                       conditions = param_combos[i,3], model= "MSstats",
                                       change = !grepl("NoChange", adjusted_models_sim1[[i]]$GlobalProtein))
}
for (i in 1:length(adjusted_models_sim1)){
  adjusted_models_sim1[[i]] <- tprFdp(adjusted_models_sim1[[i]], "pvalue", "change")
}
point_data <- tibble()
for (i in 1:24){
  df <- adjusted_models_sim1[[i]]
  row_ = sum(df$adj.pvalue < 0.05, na.rm =T)
  if (row_ == 0){
    point = tibble(x = 0, y = 0, fpr = 0,
                   sd = as.factor(df[1,]$sd), 
                   reps = as.factor(df[1,]$reps), 
                   conditions = as.factor(df[1,]$conditions),
                   model = "MSstats")
  }
  else {
  x = df[row_,]$fdp
  fpr = df[row_,]$fpr
  y = df[row_,]$tpr
  sd = as.factor(df[row_,]$sd)
  reps = as.factor(df[row_,]$reps)
  conditions = as.factor(df[row_,]$conditions)
  point = tibble(x = x, y = y, fpr = fpr, sd = sd, reps = reps, conditions = conditions, model = "MSstats")
  }
  point_data = rbind(point_data, point)
}
model_df <- do.call("rbind", adjusted_models_sim1)

model_df$reps = as.factor(model_df$reps)
model_df %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path() +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data, aes(x, y, color = reps)) +
      facet_grid(vars(sd), vars(conditions))

ROC curve

model_df <- do.call("rbind", adjusted_models_sim1)

model_df$reps = as.factor(model_df$reps)
model_df %>%
      ggplot(aes(x = fpr, y = tpr, color = reps)) +
      geom_path() +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data, aes(fpr, y, color = reps)) +
      facet_grid(vars(sd), vars(conditions))

for (i in 1:24){
  df <- adjusted_models_sim1[[i]]
  p <- df %>% ggplot(aes(x = fdp, y = tpr)) +
      geom_path() +
      geom_vline(xintercept=0.01,lty=2) +
      geom_point(data = adjusted_models_sim1[[i]][sum(adjusted_models_sim1[[i]]$adj.pvalue < 0.05, na.rm = TRUE), ],
             aes(x = fdp, y = tpr), cex = 2, color = "blue", size = 3) +
          ggtitle(paste("sd:", as.character(param_combos[i, 1]), "reps:", 
                    as.character(param_combos[i,2]), "conditions:", 
                    as.character(param_combos[i,3])))
  print(p)
}
## Warning: Duplicated aesthetics after name standardisation: size
## Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

0.2.1.2 Simulation 2

for (i in 1:length(adjusted_models_sim2)){
  adjusted_models_sim2[[i]] <- adjusted_models_sim2[[i]] %>%
                                mutate(sd = param_combos[i,1], reps = param_combos[i,2],
                                       conditions = param_combos[i,3], model= "MSstats",
                                       change = !grepl("NoChange", adjusted_models_sim2[[i]]$GlobalProtein))
}
for (i in 1:length(adjusted_models_sim2)){
  adjusted_models_sim2[[i]] <- tprFdp(adjusted_models_sim2[[i]], "pvalue", "change")
}
point_data_2 <- tibble()
for (i in 1:24){
  df <- adjusted_models_sim2[[i]]
  row_ = sum(df$adj.pvalue < 0.05, na.rm =T)
  if (row_ == 0){
    point = tibble(x = 0, y = 0, fpr = 0,
                   sd = as.factor(df[1,]$sd), 
                   reps = as.factor(df[1,]$reps), 
                   conditions = as.factor(df[1,]$conditions),
                   model = "MSstats")
  }
  else {
  x = df[row_,]$fdp
  fpr = df[row_,]$fpr
  y = df[row_,]$tpr
  sd = as.factor(df[row_,]$sd)
  reps = as.factor(df[row_,]$reps)
  conditions = as.factor(df[row_,]$conditions)
  point = tibble(x = x, y = y, fpr = fpr, sd = sd, reps = reps, conditions = conditions, model = "MSstats")
  }
  point_data_2 = rbind(point_data_2, point)
}
model_df2 <- do.call("rbind", adjusted_models_sim2)
model_df2$reps = as.factor(model_df2$reps)

model_df2 %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path() +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_2, aes(x, y, color = reps)) +
      facet_grid(vars(sd), vars(conditions))

ROC curve

model_df2 %>%
      ggplot(aes(x = fpr, y = tpr, color = reps)) +
      geom_path() +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_2, aes(fpr, y, color = reps)) +
      facet_grid(vars(sd), vars(conditions))

for (i in 1:24){
  df <- adjusted_models_sim2[[i]]
  p <- df %>% ggplot(aes(x = fdp, y = tpr)) +
      geom_path() +
      geom_vline(xintercept=0.01,lty=2) +
      geom_point(data = adjusted_models_sim2[[i]][sum(adjusted_models_sim2[[i]]$adj.pvalue < 0.05, na.rm = TRUE), ],
             aes(x = fdp, y = tpr), cex = 2, color = "blue", size = 3) +
      ggtitle(paste("sd:", as.character(param_combos[i, 1]), "reps:", 
                    as.character(param_combos[i,2]), "conditions:", 
                    as.character(param_combos[i,3])))
  print(p)
}
## Warning: Duplicated aesthetics after name standardisation: size
## Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

0.2.2 msqrob

0.2.2.1 Simulation 1

msqrob_simulation1 <- 
  msqrob_simulation1 %>%     
  rownames_to_column("Protein") %>%
  mutate(change = !grepl("NoChange", Protein),
         dataset = paste(sd, reps, conditions, sep = "_"),
         model = "msqrob"
)
msqrob_simulation1$tpr <- 1000
msqrob_simulation1$fdp <- 1000
msqrob_simulation1$fpr <- 1000
for (i in unique(msqrob_simulation1$dataset)){
  msqrob_simulation1[msqrob_simulation1$dataset==i,] <- tprFdp(msqrob_simulation1[msqrob_simulation1$dataset==i,], "pval", "change")
}
point_data_3 <- tibble()
for (i in unique(msqrob_simulation1$dataset)){
  df <- msqrob_simulation1[msqrob_simulation1$dataset==i,]
  row_ = sum(df$adjPval < 0.05, na.rm =T)
  if (row_ == 0){
    point = tibble(x = 0, y = 0, fpr = 0,
                   sd = as.factor(df[1,]$sd), 
                   reps = as.factor(df[1,]$reps), 
                   conditions = as.factor(df[1,]$conditions))
  }
  else {
  x = df[row_,]$fdp
  fpr = df[row_,]$fpr
  y = df[row_,]$tpr
  sd = as.factor(df[row_,]$sd)
  reps = as.factor(df[row_,]$reps)
  conditions = as.factor(df[row_,]$conditions)
  point = tibble(x = x, y = y, fpr = fpr, sd = sd, reps = reps, conditions = conditions, model = "msqrob")
  }
  point_data_3 = rbind(point_data_3, point)
}
msqrob_simulation1$reps = as.factor(msqrob_simulation1$reps)

msqrob_simulation1 %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path(size=0.8) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_3, aes(x, y, color = reps), size=2) +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
            axis.title=element_text(size=15),
            strip.text.x = element_text(size = 18),
            strip.text.y = element_text(size = 18),
            legend.title = element_text(size=14), 
            legend.text = element_text(size=11))

ROC curve

msqrob_simulation1$reps = as.factor(msqrob_simulation1$reps)

msqrob_simulation1 %>%
      ggplot(aes(x = fpr, y = tpr, color = reps)) +
      geom_path(size=0.8) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_3, aes(fpr, y, color = reps), size=2) +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
            axis.title=element_text(size=15),
            strip.text.x = element_text(size = 18),
            strip.text.y = element_text(size = 18),
            legend.title = element_text(size=14), 
            legend.text = element_text(size=11))

j <- 1
for (i in unique(msqrob_simulation1$dataset)){
  df <- msqrob_simulation1[msqrob_simulation1$dataset==i,]
  p <- df %>% ggplot(aes(x = fdp, y = tpr)) +
      geom_path() +
      geom_vline(xintercept=0.01,lty=2) +
      geom_point(data = df[sum(df$adjPval < 0.05, na.rm = TRUE), ],
             aes(x = fdp, y = tpr), cex = 2, color = "blue", size = 3) +
          ggtitle(paste("sd:", as.character(param_combos[j, 1]), "reps:", 
                    as.character(param_combos[j,2]), "conditions:", 
                    as.character(param_combos[j,3])))
  print(p)
  j <- j + 1
}
## Warning: Duplicated aesthetics after name standardisation: size
## Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

0.2.2.2 Simulation 2

msqrob_simulation2 <- 
  msqrob_simulation2 %>%     
  rownames_to_column("Protein") %>%
  mutate(change = !grepl("NoChange", Protein),
         dataset = paste(sd, reps, conditions, sep = "_"),
         model = "msqrob"
)
msqrob_simulation2$tpr <- 1000
msqrob_simulation2$fdp <- 1000
msqrob_simulation2$fpr <- 1000
for (i in unique(msqrob_simulation2$dataset)){
  msqrob_simulation2[msqrob_simulation2$dataset==i,] <- tprFdp(msqrob_simulation2[msqrob_simulation2$dataset==i,], "pval", "change")
}
point_data_4 <- tibble()
for (i in unique(msqrob_simulation2$dataset)){
  df <- msqrob_simulation2[msqrob_simulation2$dataset==i,]
  row_ = sum(df$adjPval < 0.05, na.rm =T)
  if (row_ == 0){
    point = tibble(x = 0, y = 0, fpr = 0,
                   sd = as.factor(df[1,]$sd), 
                   reps = as.factor(df[1,]$reps), 
                   conditions = as.factor(df[1,]$conditions))
  }
  else {
  x = df[row_,]$fdp
  fpr = df[row_,]$fpr
  y = df[row_,]$tpr
  sd = as.factor(df[row_,]$sd)
  reps = as.factor(df[row_,]$reps)
  conditions = as.factor(df[row_,]$conditions)
  point = tibble(x = x, y = y, fpr = fpr, sd = sd, reps = reps, conditions = conditions, model = "msqrob")
  }
  point_data_4 = rbind(point_data_4, point)
}
msqrob_simulation2$reps = as.factor(msqrob_simulation2$reps)

msqrob_simulation2 %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path(size=0.8) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_4, aes(x, y, color = reps), size=2) +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
      axis.title=element_text(size=15),
      strip.text.x = element_text(size = 18),
      strip.text.y = element_text(size = 18),
      legend.title = element_text(size=14), 
      legend.text = element_text(size=11))

ROC curve

msqrob_simulation2$reps = as.factor(msqrob_simulation2$reps)

msqrob_simulation2 %>%
      ggplot(aes(x = fpr, y = tpr, color = reps)) +
      geom_path(size=0.8) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_4, aes(fpr, y, color = reps), size=2) +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
      axis.title=element_text(size=15),
      strip.text.x = element_text(size = 18),
      strip.text.y = element_text(size = 18),
      legend.title = element_text(size=14), 
      legend.text = element_text(size=11))

j <- 1
for (i in unique(msqrob_simulation2$dataset)){
  df <- msqrob_simulation2[msqrob_simulation2$dataset==i,]
  p <- df %>% ggplot(aes(x = fdp, y = tpr)) +
      geom_path() +
      geom_vline(xintercept=0.01,lty=2) +
      geom_point(data = df[sum(df$adjPval < 0.05, na.rm = TRUE), ],
             aes(x = fdp, y = tpr), cex = 2, color = "blue", size = 3) +
          ggtitle(paste("sd:", as.character(param_combos[j, 1]), "reps:", 
                    as.character(param_combos[j,2]), "conditions:", 
                    as.character(param_combos[j,3])))
  print(p)
  j <- j + 1
}
## Warning: Duplicated aesthetics after name standardisation: size
## Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

## Warning: Duplicated aesthetics after name standardisation: size

0.2.3 together

0.2.3.1 Simulation 1

model_df <- model_df %>%
              dplyr::rename(logFC = log2FC,
                     se = SE,
                     t = Tvalue,
                     df = DF,
                     pval = pvalue,
                     adjPval = adj.pvalue) %>% 
              mutate(dataset = paste(sd, reps, conditions, sep = "_")) %>%
              select(-GlobalProtein)

model_df_together <- rbind(model_df, msqrob_simulation1)
point_data_together <- rbind(point_data, point_data_3)
get_point_data <- function(df, adjpval, alpha, model){
  row_ = sum(df[[adjpval]] < alpha, na.rm =T)
  if (row_ == 0){
    point = tibble(x = 0, y = 0, fpr = 0,
                   sd = as.factor(df[1,]$sd), 
                   reps = as.factor(df[1,]$reps), 
                   conditions = as.factor(df[1,]$conditions),
                   alpha = alpha,
                   model = model)
  }
  else {
    point= tibble(x = df[row_,]$fdp,
                  fpr = df[row_,]$fpr,
                  y = df[row_,]$tpr,
                  sd = as.factor(df[row_,]$sd),
                  reps = as.factor(df[row_,]$reps),
                  conditions = as.factor(df[row_,]$conditions),
                  alpha = alpha,
                  model = model)
  }
  return(point)
}
point_data_sim1_msstats <- lapply(seq(1:24), function(x) {
  p1 = get_point_data(adjusted_models_sim1[[x]], "adj.pvalue", 0.01, "MSstats")
  p5 = get_point_data(adjusted_models_sim1[[x]], "adj.pvalue", 0.05, "MSstats")
  p10 = get_point_data(adjusted_models_sim1[[x]], "adj.pvalue", 0.1, "MSstats")
  return(rbind(p1, p5, p10))
})
point_data_sim1_msstats = do.call("rbind", point_data_sim1_msstats)

point_data_sim1_msqrob <- lapply(unique(msqrob_simulation1$dataset), function(x) {
  p1 = get_point_data(msqrob_simulation1[msqrob_simulation1$dataset==x,], "adjPval", 0.01, "msqrob")
  p5 = get_point_data(msqrob_simulation1[msqrob_simulation1$dataset==x,], "adjPval", 0.05, "msqrob")
  p10 = get_point_data(msqrob_simulation1[msqrob_simulation1$dataset==x,], "adjPval", 0.1, "msqrob")
  return(rbind(p1, p5, p10))
})
point_data_sim1_msqrob = do.call("rbind", point_data_sim1_msqrob)
point_data_sim1_together = rbind(point_data_sim1_msstats, point_data_sim1_msqrob)
point_data_sim1_together$alpha <- as.factor(point_data_sim1_together$alpha)
model_df_together %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path(aes(linetype = model), size = 1) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_sim1_together, aes(x = x, y = y, shape = alpha, fill = reps), size = 1.5, color = "black") +
      facet_grid(vars(sd), vars(conditions), labeller = label_both)

Same plots, but more aesthetically pleasing (according to me, anyway)

model_df_together %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path(aes(linetype = model, size = model)) +
      scale_size_manual(values = c(msqrob = 1.15, MSstats = 0.8)) +
      scale_linetype_manual(values = c(msqrob = "solid", MSstats = "dotted")) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_together, 
                 aes(x = x, y = y, shape = model, color = reps), size = 2.5) +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
      axis.title=element_text(size=15),
      strip.text.x = element_text(size = 18),
      strip.text.y = element_text(size = 18),
      legend.title = element_text(size=14), 
      legend.text = element_text(size=11))

ROC curves

model_df_together %>%
      ggplot(aes(x = fpr, y = tpr, color = reps)) +
      geom_path(aes(linetype = model, size = model)) +
      scale_size_manual(values = c(msqrob = 1.1, MSstats = 1.1)) +
      scale_linetype_manual(values = c(msqrob = "solid", MSstats = "dotted")) +
      geom_vline(xintercept=0.05,lty=2) +
      #geom_point(data = point_data_together, aes(x = fpr, y = y, shape = model), size = 2, color = "gray30") +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
      axis.title=element_text(size=15),
      strip.text.x = element_text(size = 18),
      strip.text.y = element_text(size = 18),
      legend.title = element_text(size=14), 
      legend.text = element_text(size=11))

0.2.3.2 Simulation 2

model_df2 <- model_df2 %>%
              dplyr::rename(logFC = log2FC,
                     se = SE,
                     t = Tvalue,
                     df = DF,
                     pval = pvalue,
                     adjPval = adj.pvalue) %>% 
              mutate(dataset = paste(sd, reps, conditions, sep = "_")) %>%
              select(-GlobalProtein)

model_df2_together <- rbind(model_df2, msqrob_simulation2)
point_data_together_2 <- rbind(point_data_2, point_data_4)
point_data_sim2_msstats <- lapply(seq(1:24), function(x) {
  p1 = get_point_data(adjusted_models_sim2[[x]], "adj.pvalue", 0.01, "MSstats")
  p5 = get_point_data(adjusted_models_sim2[[x]], "adj.pvalue", 0.05, "MSstats")
  p10 = get_point_data(adjusted_models_sim2[[x]], "adj.pvalue", 0.1, "MSstats")
  return(rbind(p1, p5, p10))
})
point_data_sim2_msstats = do.call("rbind", point_data_sim2_msstats)

point_data_sim2_msqrob <- lapply(unique(msqrob_simulation2$dataset), function(x) {
  p1 = get_point_data(msqrob_simulation2[msqrob_simulation2$dataset==x,], "adjPval", 0.01, "msqrob")
  p5 = get_point_data(msqrob_simulation2[msqrob_simulation2$dataset==x,], "adjPval", 0.05, "msqrob")
  p10 = get_point_data(msqrob_simulation2[msqrob_simulation2$dataset==x,], "adjPval", 0.1, "msqrob")
  return(rbind(p1, p5, p10))
})
point_data_sim2_msqrob = do.call("rbind", point_data_sim2_msqrob)
point_data_sim2_together = rbind(point_data_sim2_msstats, point_data_sim2_msqrob)
point_data_sim2_together$alpha <- as.factor(point_data_sim2_together$alpha)
model_df2_together %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path(aes(linetype = model), size = 1) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_sim2_together, aes(x = x, y = y, shape = alpha, fill = reps), size = 1.5, color = "black") +
      facet_grid(vars(sd), vars(conditions), labeller = label_both)

model_df2_together %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path(aes(linetype = model, size = model)) +
      scale_size_manual(values = c(msqrob = 1.15, MSstats = 0.8)) +
      scale_linetype_manual(values = c(msqrob = "solid", MSstats = "dotted")) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_together_2, aes(x = x, y = y, shape = model, color = reps), size = 2.5) +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
      axis.title=element_text(size=15),
      strip.text.x = element_text(size = 18),
      strip.text.y = element_text(size = 18),
      legend.title = element_text(size=14), 
      legend.text = element_text(size=11))

ROC curves

model_df2_together %>%
      ggplot(aes(x = fpr, y = tpr, color = reps)) +
      geom_path(aes(linetype = model, size = model)) +
      scale_size_manual(values = c(msqrob = 1.1, MSstats = 1.1)) +
      scale_linetype_manual(values = c(msqrob = "solid", MSstats = "dotted")) +
      geom_vline(xintercept=0.05,lty=2) +
      #geom_point(data = point_data_together_2, aes(x = fpr, y = y, shape = model), size = 2, color = "gray35") +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
      axis.title=element_text(size=15),
      strip.text.x = element_text(size = 18),
      strip.text.y = element_text(size = 18),
      legend.title = element_text(size=14), 
      legend.text = element_text(size=11))

0.3 p value distributions

0.3.1 msstats

0.3.1.1 Simulation 1

pvals <- c()
for (i in 1:length(adjusted_models_sim1)){
  pvals <- c(pvals, df %>%
    filter(change == F) %>%
    pull(pval))

}
hist(pvals)

pvals <- c()
for (i in 1:length(adjusted_models_sim1)){
  pvals <- c(pvals, df %>%
    filter(change == T) %>%
    pull(pval))

}
hist(pvals)

0.3.2 msqrob

0.3.2.1 Simulation 1

pvals <- c()
for (i in unique(msqrob_simulation1$dataset)){
  df <- msqrob_simulation1[msqrob_simulation1$dataset==i,]
  pvals <- c(pvals, df %>%
    filter(grepl(pattern = "NoChange", Protein)) %>%
      pull(pval))
}

hist(pvals)

pvals <- c()
for (i in unique(msqrob_simulation1$dataset)){
  df <- msqrob_simulation1[msqrob_simulation1$dataset==i,]
  pvals <- c(pvals, df %>%
    filter(!grepl(pattern = "NoChange", Protein)) %>%
      pull(pval))
}

hist(pvals)

0.3.2.2 Simulation 2

pvals <- c()
for (i in unique(msqrob_simulation2$dataset)){
  df <- msqrob_simulation2[msqrob_simulation2$dataset==i,]
  pvals <- c(pvals, df %>%
    filter(grepl(pattern = "NoChange", Protein)) %>%
      pull(pval))
}

hist(pvals)

pvals <- c()
for (i in unique(msqrob_simulation2$dataset)){
  df <- msqrob_simulation2[msqrob_simulation2$dataset==i,]
  pvals <- c(pvals, df %>%
    filter(!grepl(pattern = "NoChange", Protein)) %>%
      pull(pval))
}

hist(pvals)

0.3.3 Versus when starting from summarised data

load("C:/Users/Nina/OneDrive - UGent/Documenten/Doctoraat/msqrobPTM/Datasets MSStats/simulation_data/signtable_simulation1_msqrob_comparison1_startfromsummary.rda")
sign_table11 <- sign_table
load("C:/Users/Nina/OneDrive - UGent/Documenten/Doctoraat/msqrobPTM/Datasets MSStats/simulation_data/signtable_simulation1_msqrob_comparison2_startfromsummary.rda")
sign_table12 <- sign_table
load("C:/Users/Nina/OneDrive - UGent/Documenten/Doctoraat/msqrobPTM/Datasets MSStats/simulation_data/signtable_simulation1_msqrob_comparison3_startfromsummary.rda")
sign_table13 <- sign_table

load("C:/Users/Nina/OneDrive - UGent/Documenten/Doctoraat/msqrobPTM/Datasets MSStats/simulation_data/signtable_simulation2_msqrob_comparison1_startfromsummary.rda")
sign_table21 <- sign_table
load("C:/Users/Nina/OneDrive - UGent/Documenten/Doctoraat/msqrobPTM/Datasets MSStats/simulation_data/signtable_simulation2_msqrob_comparison2_startfromsummary.rda")
sign_table22 <- sign_table
load("C:/Users/Nina/OneDrive - UGent/Documenten/Doctoraat/msqrobPTM/Datasets MSStats/simulation_data/signtable_simulation2_msqrob_comparison3_startfromsummary.rda")
sign_table23 <- sign_table
msqrob_simulation1_summary <- rbind(sign_table11, sign_table12, sign_table13)
msqrob_simulation2_summary <- rbind(sign_table21, sign_table22, sign_table23)

0.3.3.1 Simulation 1

point_data_7 <- tibble()
for (i in unique(msqrob_simulation1_summary$dataset)){
  df <- msqrob_simulation1_summary[msqrob_simulation1_summary$dataset==i,]
  row_ = sum(df$adjPval < 0.05, na.rm =T)
  if (row_ == 0){
    point = tibble(x = 0, y = 0, fpr = 0,
                   sd = as.factor(df[1,]$sd), 
                   reps = as.factor(df[1,]$reps), 
                   conditions = as.factor(df[1,]$conditions))
  }
  else {
  x = df[row_,]$fdp
  fpr = df[row_,]$fpr
  y = df[row_,]$tpr
  sd = as.factor(df[row_,]$sd)
  reps = as.factor(df[row_,]$reps)
  conditions = as.factor(df[row_,]$conditions)
  point = tibble(x = x, y = y, fpr = fpr, sd = sd, reps = reps, conditions = conditions, model = "msqrob_summaryStart")
  }
  point_data_7 = rbind(point_data_7, point)
}
msqrob_simulation1_summary <- 
  msqrob_simulation1_summary %>%     
  rownames_to_column("Protein") %>%
  mutate(change = !grepl("NoChange", Protein),
         dataset = paste(sd, reps, conditions, sep = "_"),
         model = "msqrob_summaryStart"
)
msqrob_simulation1_summary$tpr <- 1000
msqrob_simulation1_summary$fdp <- 1000
msqrob_simulation1_summary$fpr <- 1000
for (i in unique(msqrob_simulation1_summary$dataset)){
  msqrob_simulation1_summary[msqrob_simulation1_summary$dataset==i,] <- 
    tprFdp(msqrob_simulation1_summary[msqrob_simulation1_summary$dataset==i,], "pval", "change")
}
simulation1_together <- rbind(msqrob_simulation1_summary, msqrob_simulation1)
point_data_together <- rbind(point_data_3, point_data_7)
simulation1_together %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path(aes(linetype = model), size = 1) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_together, aes(x = x, y = y, shape = model), size = 1.5, color = "black") +
      facet_grid(vars(sd), vars(conditions), labeller = label_both)

simulation1_together %>%
      ggplot(aes(x = fpr, y = tpr, color = reps)) +
      geom_path(aes(linetype = model, size = model)) +
      scale_size_manual(values = c(msqrob = 1.1, msqrob_summaryStart = 1.1)) +
      scale_linetype_manual(values = c(msqrob = "solid", msqrob_summaryStart = "dotted")) +
      geom_vline(xintercept=0.05,lty=2) +
      #geom_point(data = point_data_together_2, aes(x = fpr, y = y, shape = model), size = 2, color = "gray35") +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
      axis.title=element_text(size=15),
      strip.text.x = element_text(size = 18),
      strip.text.y = element_text(size = 18),
      legend.title = element_text(size=14), 
      legend.text = element_text(size=11))

0.3.3.2 Simulation 2

point_data_8 <- tibble()
for (i in unique(msqrob_simulation2_summary$dataset)){
  df <- msqrob_simulation2_summary[msqrob_simulation2_summary$dataset==i,]
  row_ = sum(df$adjPval < 0.05, na.rm =T)
  if (row_ == 0){
    point = tibble(x = 0, y = 0, fpr = 0,
                   sd = as.factor(df[1,]$sd), 
                   reps = as.factor(df[1,]$reps), 
                   conditions = as.factor(df[1,]$conditions))
  }
  else {
  x = df[row_,]$fdp
  fpr = df[row_,]$fpr
  y = df[row_,]$tpr
  sd = as.factor(df[row_,]$sd)
  reps = as.factor(df[row_,]$reps)
  conditions = as.factor(df[row_,]$conditions)
  point = tibble(x = x, y = y, fpr = fpr, sd = sd, reps = reps, conditions = conditions, model = "msqrob_summaryStart")
  }
  point_data_8 = rbind(point_data_8, point)
}
msqrob_simulation2_summary <- 
  msqrob_simulation2_summary %>%     
  rownames_to_column("Protein") %>%
  mutate(change = !grepl("NoChange", Protein),
         dataset = paste(sd, reps, conditions, sep = "_"),
         model = "msqrob_summaryStart"
)
msqrob_simulation2_summary$tpr <- 1000
msqrob_simulation2_summary$fdp <- 1000
msqrob_simulation2_summary$fpr <- 1000
for (i in unique(msqrob_simulation2_summary$dataset)){
  msqrob_simulation2_summary[msqrob_simulation2_summary$dataset==i,] <- 
    tprFdp(msqrob_simulation2_summary[msqrob_simulation2_summary$dataset==i,], "pval", "change")
}
simulation2_together <- rbind(msqrob_simulation2_summary, msqrob_simulation2)
point_data_together <- rbind(point_data_4, point_data_8)
simulation2_together %>%
      ggplot(aes(x = fdp, y = tpr, color = reps)) +
      geom_path(aes(linetype = model), size = 1) +
      geom_vline(xintercept=0.05,lty=2) +
      geom_point(data = point_data_together, aes(x = x, y = y, shape = model), size = 1.5, color = "black") +
      facet_grid(vars(sd), vars(conditions), labeller = label_both)

simulation2_together %>%
      ggplot(aes(x = fpr, y = tpr, color = reps)) +
      geom_path(aes(linetype = model, size = model)) +
      scale_size_manual(values = c(msqrob = 1.1, msqrob_summaryStart = 1.1)) +
      scale_linetype_manual(values = c(msqrob = "solid", msqrob_summaryStart = "dotted")) +
      geom_vline(xintercept=0.05,lty=2) +
      #geom_point(data = point_data_together_2, aes(x = fpr, y = y, shape = model), size = 2, color = "gray35") +
      facet_grid(vars(conditions), vars(sd), labeller = label_both) +
      theme(axis.text=element_text(size=11),
      axis.title=element_text(size=15),
      strip.text.x = element_text(size = 18),
      strip.text.y = element_text(size = 18),
      legend.title = element_text(size=14), 
      legend.text = element_text(size=11))

LS0tDQp0aXRsZTogInNpbXVsYXRpb24gdmlzdWFsaXNhdGlvbnMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdGhlbWU6IGNvc21vDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IHllcw0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6ICc0Jw0KLS0tDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpQVE0gTEVWRUwNCg0KYGBge3IsIHNldHVwLCBpbmNsdWRlPUZ9DQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpcj0iQzovVXNlcnMvTmluYS9PbmVEcml2ZSAtIFVHZW50L0RvY3VtZW50ZW4vRG9jdG9yYWF0L21zcXJvYlBUTSBwYXBlci9zaW11bGF0aW9ucy8iKQ0KYGBgDQoNCg0KYGBge3J9DQpsb2FkKCJNU3N0YXRzX21vZGVscy9hZGp1c3RlZF9tb2RlbHNfc2ltMS5yZGEiKQ0KbG9hZCgiTVNzdGF0c19tb2RlbHMvYWRqdXN0ZWRfbW9kZWxzX3NpbTIucmRhIikNCmBgYA0KDQpgYGB7cn0NCnNpZ25fdGFibGUxMSA8LSByZWFkUkRTKCJzaW11bGF0aW9uIDEvc2lnbl90YWJsZTExLnJkcyIpDQpzaWduX3RhYmxlMTIgPC0gcmVhZFJEUygic2ltdWxhdGlvbiAxL3NpZ25fdGFibGUxMi5yZHMiKQ0Kc2lnbl90YWJsZTEzIDwtIHJlYWRSRFMoInNpbXVsYXRpb24gMS9zaWduX3RhYmxlMTMucmRzIikNCg0Kc2lnbl90YWJsZTIxIDwtIHJlYWRSRFMoInNpbXVsYXRpb24gMi9zaWduX3RhYmxlMjEucmRzIikNCnNpZ25fdGFibGUyMiA8LSByZWFkUkRTKCJzaW11bGF0aW9uIDIvc2lnbl90YWJsZTIyLnJkcyIpDQpzaWduX3RhYmxlMjMgPC0gcmVhZFJEUygic2ltdWxhdGlvbiAyL3NpZ25fdGFibGUyMy5yZHMiKQ0KYGBgDQoNCmBgYHtyfQ0KbXNxcm9iX3NpbXVsYXRpb24xIDwtIHJiaW5kKHNpZ25fdGFibGUxMSwgc2lnbl90YWJsZTEyLCBzaWduX3RhYmxlMTMpDQptc3Fyb2Jfc2ltdWxhdGlvbjIgPC0gcmJpbmQoc2lnbl90YWJsZTIxLCBzaWduX3RhYmxlMjIsIHNpZ25fdGFibGUyMykNCmBgYA0KDQoNCmBgYHtyfQ0KcyA8LSBjKC4yLC4zKQ0KcmVwcyA8LSBjKDIsMyw1LDEwKQ0KY29uZCA8LSBjKDIsMyw0KQ0KcGFyYW1fY29tYm9zIDwtIGV4cGFuZC5ncmlkKHMsIHJlcHMsIGNvbmQpDQpgYGANCg0KDQojIyBtc3N0YXRzIHR5cGUgdmlzdWFsaXNhdGlvbnMNCg0KU29tZSBvZiB0aGUgZnVuY3Rpb25zIGJlbG93IHdlcmUgZm91bmQgaW4gdGhlIHNpbXVsYXRpb24gYW5hbHlzaXMgZmlsZXMgb24gTWFzc2l2ZSBmcm9tIHRoZSBNU3N0YXRzIHRlYW0NCg0KIyMgc2Vuc2l0aXZ5IC0gZmRwIHBsb3RzDQojIyMgTVNzdGF0cw0KDQpGdW5jdGlvbiB0byBjYWxjdWxhdGUgVFBSIGFuZCBGRFANCg0KYGBge3J9DQp0cHJGZHAgPC0gZnVuY3Rpb24oZGYsIHB2YWwsIHRwKXsNCiAgb3JkIDwtIG9yZGVyKGRmW1twdmFsXV0pDQogIGRmIDwtIGRmW29yZCxdDQogIGRmJHRwciA9IGFzLm51bWVyaWMoY3Vtc3VtKGRmW1t0cF1dKS9zdW0oZGZbW3RwXV0pKQ0KICBkZiRmZHAgPSBhcy5udW1lcmljKGN1bXN1bSghZGZbW3RwXV0pLygxOmxlbmd0aChkZltbdHBdXSkpKQ0KICBkZiRmcHIgPSBhcy5udW1lcmljKGN1bXN1bSghZGZbW3RwXV0pL3N1bSghZGZbW3RwXV0pKQ0KICBkZg0KfQ0KYGBgDQoNCg0KIyMjIyBTaW11bGF0aW9uIDENCg0KYGBge3J9DQpmb3IgKGkgaW4gMTpsZW5ndGgoYWRqdXN0ZWRfbW9kZWxzX3NpbTEpKXsNCiAgYWRqdXN0ZWRfbW9kZWxzX3NpbTFbW2ldXSA8LSBhZGp1c3RlZF9tb2RlbHNfc2ltMVtbaV1dICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoc2QgPSBwYXJhbV9jb21ib3NbaSwxXSwgcmVwcyA9IHBhcmFtX2NvbWJvc1tpLDJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9ucyA9IHBhcmFtX2NvbWJvc1tpLDNdLCBtb2RlbD0gIk1Tc3RhdHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hhbmdlID0gIWdyZXBsKCJOb0NoYW5nZSIsIGFkanVzdGVkX21vZGVsc19zaW0xW1tpXV0kR2xvYmFsUHJvdGVpbikpDQp9DQpgYGANCg0KYGBge3J9DQpmb3IgKGkgaW4gMTpsZW5ndGgoYWRqdXN0ZWRfbW9kZWxzX3NpbTEpKXsNCiAgYWRqdXN0ZWRfbW9kZWxzX3NpbTFbW2ldXSA8LSB0cHJGZHAoYWRqdXN0ZWRfbW9kZWxzX3NpbTFbW2ldXSwgInB2YWx1ZSIsICJjaGFuZ2UiKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KcG9pbnRfZGF0YSA8LSB0aWJibGUoKQ0KZm9yIChpIGluIDE6MjQpew0KICBkZiA8LSBhZGp1c3RlZF9tb2RlbHNfc2ltMVtbaV1dDQogIHJvd18gPSBzdW0oZGYkYWRqLnB2YWx1ZSA8IDAuMDUsIG5hLnJtID1UKQ0KICBpZiAocm93XyA9PSAwKXsNCiAgICBwb2ludCA9IHRpYmJsZSh4ID0gMCwgeSA9IDAsIGZwciA9IDAsDQogICAgICAgICAgICAgICAgICAgc2QgPSBhcy5mYWN0b3IoZGZbMSxdJHNkKSwgDQogICAgICAgICAgICAgICAgICAgcmVwcyA9IGFzLmZhY3RvcihkZlsxLF0kcmVwcyksIA0KICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbnMgPSBhcy5mYWN0b3IoZGZbMSxdJGNvbmRpdGlvbnMpLA0KICAgICAgICAgICAgICAgICAgIG1vZGVsID0gIk1Tc3RhdHMiKQ0KICB9DQogIGVsc2Ugew0KICB4ID0gZGZbcm93XyxdJGZkcA0KICBmcHIgPSBkZltyb3dfLF0kZnByDQogIHkgPSBkZltyb3dfLF0kdHByDQogIHNkID0gYXMuZmFjdG9yKGRmW3Jvd18sXSRzZCkNCiAgcmVwcyA9IGFzLmZhY3RvcihkZltyb3dfLF0kcmVwcykNCiAgY29uZGl0aW9ucyA9IGFzLmZhY3RvcihkZltyb3dfLF0kY29uZGl0aW9ucykNCiAgcG9pbnQgPSB0aWJibGUoeCA9IHgsIHkgPSB5LCBmcHIgPSBmcHIsIHNkID0gc2QsIHJlcHMgPSByZXBzLCBjb25kaXRpb25zID0gY29uZGl0aW9ucywgbW9kZWwgPSAiTVNzdGF0cyIpDQogIH0NCiAgcG9pbnRfZGF0YSA9IHJiaW5kKHBvaW50X2RhdGEsIHBvaW50KQ0KfQ0KYGBgDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMn0NCm1vZGVsX2RmIDwtIGRvLmNhbGwoInJiaW5kIiwgYWRqdXN0ZWRfbW9kZWxzX3NpbTEpDQoNCm1vZGVsX2RmJHJlcHMgPSBhcy5mYWN0b3IobW9kZWxfZGYkcmVwcykNCm1vZGVsX2RmICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZmRwLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoKSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wNSxsdHk9MikgKw0KICAgICAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfZGF0YSwgYWVzKHgsIHksIGNvbG9yID0gcmVwcykpICsNCiAgICAgIGZhY2V0X2dyaWQodmFycyhzZCksIHZhcnMoY29uZGl0aW9ucykpDQpgYGANCg0KUk9DIGN1cnZlDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMn0NCm1vZGVsX2RmIDwtIGRvLmNhbGwoInJiaW5kIiwgYWRqdXN0ZWRfbW9kZWxzX3NpbTEpDQoNCm1vZGVsX2RmJHJlcHMgPSBhcy5mYWN0b3IobW9kZWxfZGYkcmVwcykNCm1vZGVsX2RmICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZnByLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoKSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wNSxsdHk9MikgKw0KICAgICAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfZGF0YSwgYWVzKGZwciwgeSwgY29sb3IgPSByZXBzKSkgKw0KICAgICAgZmFjZXRfZ3JpZCh2YXJzKHNkKSwgdmFycyhjb25kaXRpb25zKSkNCmBgYA0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOjI0KXsNCiAgZGYgPC0gYWRqdXN0ZWRfbW9kZWxzX3NpbTFbW2ldXQ0KICBwIDwtIGRmICU+JSBnZ3Bsb3QoYWVzKHggPSBmZHAsIHkgPSB0cHIpKSArDQogICAgICBnZW9tX3BhdGgoKSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wMSxsdHk9MikgKw0KICAgICAgZ2VvbV9wb2ludChkYXRhID0gYWRqdXN0ZWRfbW9kZWxzX3NpbTFbW2ldXVtzdW0oYWRqdXN0ZWRfbW9kZWxzX3NpbTFbW2ldXSRhZGoucHZhbHVlIDwgMC4wNSwgbmEucm0gPSBUUlVFKSwgXSwNCiAgICAgICAgICAgICBhZXMoeCA9IGZkcCwgeSA9IHRwciksIGNleCA9IDIsIGNvbG9yID0gImJsdWUiLCBzaXplID0gMykgKw0KICAgICAgICAgIGdndGl0bGUocGFzdGUoInNkOiIsIGFzLmNoYXJhY3RlcihwYXJhbV9jb21ib3NbaSwgMV0pLCAicmVwczoiLCANCiAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHBhcmFtX2NvbWJvc1tpLDJdKSwgImNvbmRpdGlvbnM6IiwgDQogICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihwYXJhbV9jb21ib3NbaSwzXSkpKQ0KICBwcmludChwKQ0KfQ0KYGBgDQoNCg0KDQojIyMjIFNpbXVsYXRpb24gMg0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOmxlbmd0aChhZGp1c3RlZF9tb2RlbHNfc2ltMikpew0KICBhZGp1c3RlZF9tb2RlbHNfc2ltMltbaV1dIDwtIGFkanVzdGVkX21vZGVsc19zaW0yW1tpXV0gJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShzZCA9IHBhcmFtX2NvbWJvc1tpLDFdLCByZXBzID0gcGFyYW1fY29tYm9zW2ksMl0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25kaXRpb25zID0gcGFyYW1fY29tYm9zW2ksM10sIG1vZGVsPSAiTVNzdGF0cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGFuZ2UgPSAhZ3JlcGwoIk5vQ2hhbmdlIiwgYWRqdXN0ZWRfbW9kZWxzX3NpbTJbW2ldXSRHbG9iYWxQcm90ZWluKSkNCn0NCmBgYA0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOmxlbmd0aChhZGp1c3RlZF9tb2RlbHNfc2ltMikpew0KICBhZGp1c3RlZF9tb2RlbHNfc2ltMltbaV1dIDwtIHRwckZkcChhZGp1c3RlZF9tb2RlbHNfc2ltMltbaV1dLCAicHZhbHVlIiwgImNoYW5nZSIpDQp9DQpgYGANCg0KYGBge3J9DQpwb2ludF9kYXRhXzIgPC0gdGliYmxlKCkNCmZvciAoaSBpbiAxOjI0KXsNCiAgZGYgPC0gYWRqdXN0ZWRfbW9kZWxzX3NpbTJbW2ldXQ0KICByb3dfID0gc3VtKGRmJGFkai5wdmFsdWUgPCAwLjA1LCBuYS5ybSA9VCkNCiAgaWYgKHJvd18gPT0gMCl7DQogICAgcG9pbnQgPSB0aWJibGUoeCA9IDAsIHkgPSAwLCBmcHIgPSAwLA0KICAgICAgICAgICAgICAgICAgIHNkID0gYXMuZmFjdG9yKGRmWzEsXSRzZCksIA0KICAgICAgICAgICAgICAgICAgIHJlcHMgPSBhcy5mYWN0b3IoZGZbMSxdJHJlcHMpLCANCiAgICAgICAgICAgICAgICAgICBjb25kaXRpb25zID0gYXMuZmFjdG9yKGRmWzEsXSRjb25kaXRpb25zKSwNCiAgICAgICAgICAgICAgICAgICBtb2RlbCA9ICJNU3N0YXRzIikNCiAgfQ0KICBlbHNlIHsNCiAgeCA9IGRmW3Jvd18sXSRmZHANCiAgZnByID0gZGZbcm93XyxdJGZwcg0KICB5ID0gZGZbcm93XyxdJHRwcg0KICBzZCA9IGFzLmZhY3RvcihkZltyb3dfLF0kc2QpDQogIHJlcHMgPSBhcy5mYWN0b3IoZGZbcm93XyxdJHJlcHMpDQogIGNvbmRpdGlvbnMgPSBhcy5mYWN0b3IoZGZbcm93XyxdJGNvbmRpdGlvbnMpDQogIHBvaW50ID0gdGliYmxlKHggPSB4LCB5ID0geSwgZnByID0gZnByLCBzZCA9IHNkLCByZXBzID0gcmVwcywgY29uZGl0aW9ucyA9IGNvbmRpdGlvbnMsIG1vZGVsID0gIk1Tc3RhdHMiKQ0KICB9DQogIHBvaW50X2RhdGFfMiA9IHJiaW5kKHBvaW50X2RhdGFfMiwgcG9pbnQpDQp9DQpgYGANCg0KYGBge3IsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEyfQ0KbW9kZWxfZGYyIDwtIGRvLmNhbGwoInJiaW5kIiwgYWRqdXN0ZWRfbW9kZWxzX3NpbTIpDQptb2RlbF9kZjIkcmVwcyA9IGFzLmZhY3Rvcihtb2RlbF9kZjIkcmVwcykNCg0KbW9kZWxfZGYyICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZmRwLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoKSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wNSxsdHk9MikgKw0KICAgICAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfZGF0YV8yLCBhZXMoeCwgeSwgY29sb3IgPSByZXBzKSkgKw0KICAgICAgZmFjZXRfZ3JpZCh2YXJzKHNkKSwgdmFycyhjb25kaXRpb25zKSkNCmBgYA0KDQpST0MgY3VydmUNCg0KYGBge3IsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEyfQ0KbW9kZWxfZGYyICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZnByLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoKSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wNSxsdHk9MikgKw0KICAgICAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfZGF0YV8yLCBhZXMoZnByLCB5LCBjb2xvciA9IHJlcHMpKSArDQogICAgICBmYWNldF9ncmlkKHZhcnMoc2QpLCB2YXJzKGNvbmRpdGlvbnMpKQ0KYGBgDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6MjQpew0KICBkZiA8LSBhZGp1c3RlZF9tb2RlbHNfc2ltMltbaV1dDQogIHAgPC0gZGYgJT4lIGdncGxvdChhZXMoeCA9IGZkcCwgeSA9IHRwcikpICsNCiAgICAgIGdlb21fcGF0aCgpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLjAxLGx0eT0yKSArDQogICAgICBnZW9tX3BvaW50KGRhdGEgPSBhZGp1c3RlZF9tb2RlbHNfc2ltMltbaV1dW3N1bShhZGp1c3RlZF9tb2RlbHNfc2ltMltbaV1dJGFkai5wdmFsdWUgPCAwLjA1LCBuYS5ybSA9IFRSVUUpLCBdLA0KICAgICAgICAgICAgIGFlcyh4ID0gZmRwLCB5ID0gdHByKSwgY2V4ID0gMiwgY29sb3IgPSAiYmx1ZSIsIHNpemUgPSAzKSArDQogICAgICBnZ3RpdGxlKHBhc3RlKCJzZDoiLCBhcy5jaGFyYWN0ZXIocGFyYW1fY29tYm9zW2ksIDFdKSwgInJlcHM6IiwgDQogICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihwYXJhbV9jb21ib3NbaSwyXSksICJjb25kaXRpb25zOiIsIA0KICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocGFyYW1fY29tYm9zW2ksM10pKSkNCiAgcHJpbnQocCkNCn0NCmBgYA0KDQojIyMgbXNxcm9iDQoNCiMjIyMgU2ltdWxhdGlvbiAxDQoNCmBgYHtyfQ0KbXNxcm9iX3NpbXVsYXRpb24xIDwtIA0KICBtc3Fyb2Jfc2ltdWxhdGlvbjEgJT4lICAgICANCiAgcm93bmFtZXNfdG9fY29sdW1uKCJQcm90ZWluIikgJT4lDQogIG11dGF0ZShjaGFuZ2UgPSAhZ3JlcGwoIk5vQ2hhbmdlIiwgUHJvdGVpbiksDQogICAgICAgICBkYXRhc2V0ID0gcGFzdGUoc2QsIHJlcHMsIGNvbmRpdGlvbnMsIHNlcCA9ICJfIiksDQogICAgICAgICBtb2RlbCA9ICJtc3Fyb2IiDQopDQpgYGANCg0KYGBge3J9DQptc3Fyb2Jfc2ltdWxhdGlvbjEkdHByIDwtIDEwMDANCm1zcXJvYl9zaW11bGF0aW9uMSRmZHAgPC0gMTAwMA0KbXNxcm9iX3NpbXVsYXRpb24xJGZwciA8LSAxMDAwDQpmb3IgKGkgaW4gdW5pcXVlKG1zcXJvYl9zaW11bGF0aW9uMSRkYXRhc2V0KSl7DQogIG1zcXJvYl9zaW11bGF0aW9uMVttc3Fyb2Jfc2ltdWxhdGlvbjEkZGF0YXNldD09aSxdIDwtIHRwckZkcChtc3Fyb2Jfc2ltdWxhdGlvbjFbbXNxcm9iX3NpbXVsYXRpb24xJGRhdGFzZXQ9PWksXSwgInB2YWwiLCAiY2hhbmdlIikNCn0NCmBgYA0KDQpgYGB7cn0NCnBvaW50X2RhdGFfMyA8LSB0aWJibGUoKQ0KZm9yIChpIGluIHVuaXF1ZShtc3Fyb2Jfc2ltdWxhdGlvbjEkZGF0YXNldCkpew0KICBkZiA8LSBtc3Fyb2Jfc2ltdWxhdGlvbjFbbXNxcm9iX3NpbXVsYXRpb24xJGRhdGFzZXQ9PWksXQ0KICByb3dfID0gc3VtKGRmJGFkalB2YWwgPCAwLjA1LCBuYS5ybSA9VCkNCiAgaWYgKHJvd18gPT0gMCl7DQogICAgcG9pbnQgPSB0aWJibGUoeCA9IDAsIHkgPSAwLCBmcHIgPSAwLA0KICAgICAgICAgICAgICAgICAgIHNkID0gYXMuZmFjdG9yKGRmWzEsXSRzZCksIA0KICAgICAgICAgICAgICAgICAgIHJlcHMgPSBhcy5mYWN0b3IoZGZbMSxdJHJlcHMpLCANCiAgICAgICAgICAgICAgICAgICBjb25kaXRpb25zID0gYXMuZmFjdG9yKGRmWzEsXSRjb25kaXRpb25zKSkNCiAgfQ0KICBlbHNlIHsNCiAgeCA9IGRmW3Jvd18sXSRmZHANCiAgZnByID0gZGZbcm93XyxdJGZwcg0KICB5ID0gZGZbcm93XyxdJHRwcg0KICBzZCA9IGFzLmZhY3RvcihkZltyb3dfLF0kc2QpDQogIHJlcHMgPSBhcy5mYWN0b3IoZGZbcm93XyxdJHJlcHMpDQogIGNvbmRpdGlvbnMgPSBhcy5mYWN0b3IoZGZbcm93XyxdJGNvbmRpdGlvbnMpDQogIHBvaW50ID0gdGliYmxlKHggPSB4LCB5ID0geSwgZnByID0gZnByLCBzZCA9IHNkLCByZXBzID0gcmVwcywgY29uZGl0aW9ucyA9IGNvbmRpdGlvbnMsIG1vZGVsID0gIm1zcXJvYiIpDQogIH0NCiAgcG9pbnRfZGF0YV8zID0gcmJpbmQocG9pbnRfZGF0YV8zLCBwb2ludCkNCn0NCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTExfQ0KbXNxcm9iX3NpbXVsYXRpb24xJHJlcHMgPSBhcy5mYWN0b3IobXNxcm9iX3NpbXVsYXRpb24xJHJlcHMpDQoNCm1zcXJvYl9zaW11bGF0aW9uMSAlPiUNCiAgICAgIGdncGxvdChhZXMoeCA9IGZkcCwgeSA9IHRwciwgY29sb3IgPSByZXBzKSkgKw0KICAgICAgZ2VvbV9wYXRoKHNpemU9MC44KSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wNSxsdHk9MikgKw0KICAgICAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfZGF0YV8zLCBhZXMoeCwgeSwgY29sb3IgPSByZXBzKSwgc2l6ZT0yKSArDQogICAgICBmYWNldF9ncmlkKHZhcnMoY29uZGl0aW9ucyksIHZhcnMoc2QpLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsNCiAgICAgIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE1KSwNCiAgICAgICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksDQogICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksIA0KICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSkpDQpgYGANCg0KUk9DIGN1cnZlDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTF9DQptc3Fyb2Jfc2ltdWxhdGlvbjEkcmVwcyA9IGFzLmZhY3Rvcihtc3Fyb2Jfc2ltdWxhdGlvbjEkcmVwcykNCg0KbXNxcm9iX3NpbXVsYXRpb24xICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZnByLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoc2l6ZT0wLjgpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLjA1LGx0eT0yKSArDQogICAgICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9kYXRhXzMsIGFlcyhmcHIsIHksIGNvbG9yID0gcmVwcyksIHNpemU9MikgKw0KICAgICAgZmFjZXRfZ3JpZCh2YXJzKGNvbmRpdGlvbnMpLCB2YXJzKHNkKSwgbGFiZWxsZXIgPSBsYWJlbF9ib3RoKSArDQogICAgICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTEpLA0KICAgICAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNSksDQogICAgICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwNCiAgICAgICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLCANCiAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTEpKQ0KYGBgDQoNCmBgYHtyfQ0KaiA8LSAxDQpmb3IgKGkgaW4gdW5pcXVlKG1zcXJvYl9zaW11bGF0aW9uMSRkYXRhc2V0KSl7DQogIGRmIDwtIG1zcXJvYl9zaW11bGF0aW9uMVttc3Fyb2Jfc2ltdWxhdGlvbjEkZGF0YXNldD09aSxdDQogIHAgPC0gZGYgJT4lIGdncGxvdChhZXMoeCA9IGZkcCwgeSA9IHRwcikpICsNCiAgICAgIGdlb21fcGF0aCgpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLjAxLGx0eT0yKSArDQogICAgICBnZW9tX3BvaW50KGRhdGEgPSBkZltzdW0oZGYkYWRqUHZhbCA8IDAuMDUsIG5hLnJtID0gVFJVRSksIF0sDQogICAgICAgICAgICAgYWVzKHggPSBmZHAsIHkgPSB0cHIpLCBjZXggPSAyLCBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDMpICsNCiAgICAgICAgICBnZ3RpdGxlKHBhc3RlKCJzZDoiLCBhcy5jaGFyYWN0ZXIocGFyYW1fY29tYm9zW2osIDFdKSwgInJlcHM6IiwgDQogICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihwYXJhbV9jb21ib3NbaiwyXSksICJjb25kaXRpb25zOiIsIA0KICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocGFyYW1fY29tYm9zW2osM10pKSkNCiAgcHJpbnQocCkNCiAgaiA8LSBqICsgMQ0KfQ0KYGBgDQoNCiMjIyMgU2ltdWxhdGlvbiAyDQoNCmBgYHtyfQ0KbXNxcm9iX3NpbXVsYXRpb24yIDwtIA0KICBtc3Fyb2Jfc2ltdWxhdGlvbjIgJT4lICAgICANCiAgcm93bmFtZXNfdG9fY29sdW1uKCJQcm90ZWluIikgJT4lDQogIG11dGF0ZShjaGFuZ2UgPSAhZ3JlcGwoIk5vQ2hhbmdlIiwgUHJvdGVpbiksDQogICAgICAgICBkYXRhc2V0ID0gcGFzdGUoc2QsIHJlcHMsIGNvbmRpdGlvbnMsIHNlcCA9ICJfIiksDQogICAgICAgICBtb2RlbCA9ICJtc3Fyb2IiDQopDQpgYGANCg0KYGBge3J9DQptc3Fyb2Jfc2ltdWxhdGlvbjIkdHByIDwtIDEwMDANCm1zcXJvYl9zaW11bGF0aW9uMiRmZHAgPC0gMTAwMA0KbXNxcm9iX3NpbXVsYXRpb24yJGZwciA8LSAxMDAwDQpmb3IgKGkgaW4gdW5pcXVlKG1zcXJvYl9zaW11bGF0aW9uMiRkYXRhc2V0KSl7DQogIG1zcXJvYl9zaW11bGF0aW9uMlttc3Fyb2Jfc2ltdWxhdGlvbjIkZGF0YXNldD09aSxdIDwtIHRwckZkcChtc3Fyb2Jfc2ltdWxhdGlvbjJbbXNxcm9iX3NpbXVsYXRpb24yJGRhdGFzZXQ9PWksXSwgInB2YWwiLCAiY2hhbmdlIikNCn0NCmBgYA0KDQpgYGB7cn0NCnBvaW50X2RhdGFfNCA8LSB0aWJibGUoKQ0KZm9yIChpIGluIHVuaXF1ZShtc3Fyb2Jfc2ltdWxhdGlvbjIkZGF0YXNldCkpew0KICBkZiA8LSBtc3Fyb2Jfc2ltdWxhdGlvbjJbbXNxcm9iX3NpbXVsYXRpb24yJGRhdGFzZXQ9PWksXQ0KICByb3dfID0gc3VtKGRmJGFkalB2YWwgPCAwLjA1LCBuYS5ybSA9VCkNCiAgaWYgKHJvd18gPT0gMCl7DQogICAgcG9pbnQgPSB0aWJibGUoeCA9IDAsIHkgPSAwLCBmcHIgPSAwLA0KICAgICAgICAgICAgICAgICAgIHNkID0gYXMuZmFjdG9yKGRmWzEsXSRzZCksIA0KICAgICAgICAgICAgICAgICAgIHJlcHMgPSBhcy5mYWN0b3IoZGZbMSxdJHJlcHMpLCANCiAgICAgICAgICAgICAgICAgICBjb25kaXRpb25zID0gYXMuZmFjdG9yKGRmWzEsXSRjb25kaXRpb25zKSkNCiAgfQ0KICBlbHNlIHsNCiAgeCA9IGRmW3Jvd18sXSRmZHANCiAgZnByID0gZGZbcm93XyxdJGZwcg0KICB5ID0gZGZbcm93XyxdJHRwcg0KICBzZCA9IGFzLmZhY3RvcihkZltyb3dfLF0kc2QpDQogIHJlcHMgPSBhcy5mYWN0b3IoZGZbcm93XyxdJHJlcHMpDQogIGNvbmRpdGlvbnMgPSBhcy5mYWN0b3IoZGZbcm93XyxdJGNvbmRpdGlvbnMpDQogIHBvaW50ID0gdGliYmxlKHggPSB4LCB5ID0geSwgZnByID0gZnByLCBzZCA9IHNkLCByZXBzID0gcmVwcywgY29uZGl0aW9ucyA9IGNvbmRpdGlvbnMsIG1vZGVsID0gIm1zcXJvYiIpDQogIH0NCiAgcG9pbnRfZGF0YV80ID0gcmJpbmQocG9pbnRfZGF0YV80LCBwb2ludCkNCn0NCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTExfQ0KbXNxcm9iX3NpbXVsYXRpb24yJHJlcHMgPSBhcy5mYWN0b3IobXNxcm9iX3NpbXVsYXRpb24yJHJlcHMpDQoNCm1zcXJvYl9zaW11bGF0aW9uMiAlPiUNCiAgICAgIGdncGxvdChhZXMoeCA9IGZkcCwgeSA9IHRwciwgY29sb3IgPSByZXBzKSkgKw0KICAgICAgZ2VvbV9wYXRoKHNpemU9MC44KSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wNSxsdHk9MikgKw0KICAgICAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfZGF0YV80LCBhZXMoeCwgeSwgY29sb3IgPSByZXBzKSwgc2l6ZT0yKSArDQogICAgICBmYWNldF9ncmlkKHZhcnMoY29uZGl0aW9ucyksIHZhcnMoc2QpLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsNCiAgICAgIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE1KSwNCiAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksDQogICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksIA0KICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSkpDQpgYGANCg0KUk9DIGN1cnZlDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTF9DQptc3Fyb2Jfc2ltdWxhdGlvbjIkcmVwcyA9IGFzLmZhY3Rvcihtc3Fyb2Jfc2ltdWxhdGlvbjIkcmVwcykNCg0KbXNxcm9iX3NpbXVsYXRpb24yICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZnByLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoc2l6ZT0wLjgpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLjA1LGx0eT0yKSArDQogICAgICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9kYXRhXzQsIGFlcyhmcHIsIHksIGNvbG9yID0gcmVwcyksIHNpemU9MikgKw0KICAgICAgZmFjZXRfZ3JpZCh2YXJzKGNvbmRpdGlvbnMpLCB2YXJzKHNkKSwgbGFiZWxsZXIgPSBsYWJlbF9ib3RoKSArDQogICAgICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTEpLA0KICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNSksDQogICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwNCiAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLCANCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTEpKQ0KYGBgDQoNCmBgYHtyfQ0KaiA8LSAxDQpmb3IgKGkgaW4gdW5pcXVlKG1zcXJvYl9zaW11bGF0aW9uMiRkYXRhc2V0KSl7DQogIGRmIDwtIG1zcXJvYl9zaW11bGF0aW9uMlttc3Fyb2Jfc2ltdWxhdGlvbjIkZGF0YXNldD09aSxdDQogIHAgPC0gZGYgJT4lIGdncGxvdChhZXMoeCA9IGZkcCwgeSA9IHRwcikpICsNCiAgICAgIGdlb21fcGF0aCgpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLjAxLGx0eT0yKSArDQogICAgICBnZW9tX3BvaW50KGRhdGEgPSBkZltzdW0oZGYkYWRqUHZhbCA8IDAuMDUsIG5hLnJtID0gVFJVRSksIF0sDQogICAgICAgICAgICAgYWVzKHggPSBmZHAsIHkgPSB0cHIpLCBjZXggPSAyLCBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDMpICsNCiAgICAgICAgICBnZ3RpdGxlKHBhc3RlKCJzZDoiLCBhcy5jaGFyYWN0ZXIocGFyYW1fY29tYm9zW2osIDFdKSwgInJlcHM6IiwgDQogICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihwYXJhbV9jb21ib3NbaiwyXSksICJjb25kaXRpb25zOiIsIA0KICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocGFyYW1fY29tYm9zW2osM10pKSkNCiAgcHJpbnQocCkNCiAgaiA8LSBqICsgMQ0KfQ0KYGBgDQoNCiMjIyB0b2dldGhlcg0KDQojIyMjIFNpbXVsYXRpb24gMQ0KDQpgYGB7cn0NCm1vZGVsX2RmIDwtIG1vZGVsX2RmICU+JQ0KICAgICAgICAgICAgICBkcGx5cjo6cmVuYW1lKGxvZ0ZDID0gbG9nMkZDLA0KICAgICAgICAgICAgICAgICAgICAgc2UgPSBTRSwNCiAgICAgICAgICAgICAgICAgICAgIHQgPSBUdmFsdWUsDQogICAgICAgICAgICAgICAgICAgICBkZiA9IERGLA0KICAgICAgICAgICAgICAgICAgICAgcHZhbCA9IHB2YWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgIGFkalB2YWwgPSBhZGoucHZhbHVlKSAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShkYXRhc2V0ID0gcGFzdGUoc2QsIHJlcHMsIGNvbmRpdGlvbnMsIHNlcCA9ICJfIikpICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoLUdsb2JhbFByb3RlaW4pDQoNCm1vZGVsX2RmX3RvZ2V0aGVyIDwtIHJiaW5kKG1vZGVsX2RmLCBtc3Fyb2Jfc2ltdWxhdGlvbjEpDQpgYGANCg0KYGBge3J9DQpwb2ludF9kYXRhX3RvZ2V0aGVyIDwtIHJiaW5kKHBvaW50X2RhdGEsIHBvaW50X2RhdGFfMykNCmBgYA0KDQpgYGB7cn0NCmdldF9wb2ludF9kYXRhIDwtIGZ1bmN0aW9uKGRmLCBhZGpwdmFsLCBhbHBoYSwgbW9kZWwpew0KICByb3dfID0gc3VtKGRmW1thZGpwdmFsXV0gPCBhbHBoYSwgbmEucm0gPVQpDQogIGlmIChyb3dfID09IDApew0KICAgIHBvaW50ID0gdGliYmxlKHggPSAwLCB5ID0gMCwgZnByID0gMCwNCiAgICAgICAgICAgICAgICAgICBzZCA9IGFzLmZhY3RvcihkZlsxLF0kc2QpLCANCiAgICAgICAgICAgICAgICAgICByZXBzID0gYXMuZmFjdG9yKGRmWzEsXSRyZXBzKSwgDQogICAgICAgICAgICAgICAgICAgY29uZGl0aW9ucyA9IGFzLmZhY3RvcihkZlsxLF0kY29uZGl0aW9ucyksDQogICAgICAgICAgICAgICAgICAgYWxwaGEgPSBhbHBoYSwNCiAgICAgICAgICAgICAgICAgICBtb2RlbCA9IG1vZGVsKQ0KICB9DQogIGVsc2Ugew0KICAgIHBvaW50PSB0aWJibGUoeCA9IGRmW3Jvd18sXSRmZHAsDQogICAgICAgICAgICAgICAgICBmcHIgPSBkZltyb3dfLF0kZnByLA0KICAgICAgICAgICAgICAgICAgeSA9IGRmW3Jvd18sXSR0cHIsDQogICAgICAgICAgICAgICAgICBzZCA9IGFzLmZhY3RvcihkZltyb3dfLF0kc2QpLA0KICAgICAgICAgICAgICAgICAgcmVwcyA9IGFzLmZhY3RvcihkZltyb3dfLF0kcmVwcyksDQogICAgICAgICAgICAgICAgICBjb25kaXRpb25zID0gYXMuZmFjdG9yKGRmW3Jvd18sXSRjb25kaXRpb25zKSwNCiAgICAgICAgICAgICAgICAgIGFscGhhID0gYWxwaGEsDQogICAgICAgICAgICAgICAgICBtb2RlbCA9IG1vZGVsKQ0KICB9DQogIHJldHVybihwb2ludCkNCn0NCg0KYGBgDQoNCmBgYHtyfQ0KcG9pbnRfZGF0YV9zaW0xX21zc3RhdHMgPC0gbGFwcGx5KHNlcSgxOjI0KSwgZnVuY3Rpb24oeCkgew0KICBwMSA9IGdldF9wb2ludF9kYXRhKGFkanVzdGVkX21vZGVsc19zaW0xW1t4XV0sICJhZGoucHZhbHVlIiwgMC4wMSwgIk1Tc3RhdHMiKQ0KICBwNSA9IGdldF9wb2ludF9kYXRhKGFkanVzdGVkX21vZGVsc19zaW0xW1t4XV0sICJhZGoucHZhbHVlIiwgMC4wNSwgIk1Tc3RhdHMiKQ0KICBwMTAgPSBnZXRfcG9pbnRfZGF0YShhZGp1c3RlZF9tb2RlbHNfc2ltMVtbeF1dLCAiYWRqLnB2YWx1ZSIsIDAuMSwgIk1Tc3RhdHMiKQ0KICByZXR1cm4ocmJpbmQocDEsIHA1LCBwMTApKQ0KfSkNCnBvaW50X2RhdGFfc2ltMV9tc3N0YXRzID0gZG8uY2FsbCgicmJpbmQiLCBwb2ludF9kYXRhX3NpbTFfbXNzdGF0cykNCg0KcG9pbnRfZGF0YV9zaW0xX21zcXJvYiA8LSBsYXBwbHkodW5pcXVlKG1zcXJvYl9zaW11bGF0aW9uMSRkYXRhc2V0KSwgZnVuY3Rpb24oeCkgew0KICBwMSA9IGdldF9wb2ludF9kYXRhKG1zcXJvYl9zaW11bGF0aW9uMVttc3Fyb2Jfc2ltdWxhdGlvbjEkZGF0YXNldD09eCxdLCAiYWRqUHZhbCIsIDAuMDEsICJtc3Fyb2IiKQ0KICBwNSA9IGdldF9wb2ludF9kYXRhKG1zcXJvYl9zaW11bGF0aW9uMVttc3Fyb2Jfc2ltdWxhdGlvbjEkZGF0YXNldD09eCxdLCAiYWRqUHZhbCIsIDAuMDUsICJtc3Fyb2IiKQ0KICBwMTAgPSBnZXRfcG9pbnRfZGF0YShtc3Fyb2Jfc2ltdWxhdGlvbjFbbXNxcm9iX3NpbXVsYXRpb24xJGRhdGFzZXQ9PXgsXSwgImFkalB2YWwiLCAwLjEsICJtc3Fyb2IiKQ0KICByZXR1cm4ocmJpbmQocDEsIHA1LCBwMTApKQ0KfSkNCnBvaW50X2RhdGFfc2ltMV9tc3Fyb2IgPSBkby5jYWxsKCJyYmluZCIsIHBvaW50X2RhdGFfc2ltMV9tc3Fyb2IpDQpwb2ludF9kYXRhX3NpbTFfdG9nZXRoZXIgPSByYmluZChwb2ludF9kYXRhX3NpbTFfbXNzdGF0cywgcG9pbnRfZGF0YV9zaW0xX21zcXJvYikNCnBvaW50X2RhdGFfc2ltMV90b2dldGhlciRhbHBoYSA8LSBhcy5mYWN0b3IocG9pbnRfZGF0YV9zaW0xX3RvZ2V0aGVyJGFscGhhKQ0KYGBgDQoNCg0KYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xM30NCm1vZGVsX2RmX3RvZ2V0aGVyICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZmRwLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoYWVzKGxpbmV0eXBlID0gbW9kZWwpLCBzaXplID0gMSkgKw0KICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAuMDUsbHR5PTIpICsNCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHBvaW50X2RhdGFfc2ltMV90b2dldGhlciwgYWVzKHggPSB4LCB5ID0geSwgc2hhcGUgPSBhbHBoYSwgZmlsbCA9IHJlcHMpLCBzaXplID0gMS41LCBjb2xvciA9ICJibGFjayIpICsNCiAgICAgIGZhY2V0X2dyaWQodmFycyhzZCksIHZhcnMoY29uZGl0aW9ucyksIGxhYmVsbGVyID0gbGFiZWxfYm90aCkNCmBgYA0KDQpTYW1lIHBsb3RzLCBidXQgbW9yZSBhZXN0aGV0aWNhbGx5IHBsZWFzaW5nIChhY2NvcmRpbmcgdG8gbWUsIGFueXdheSkNCg0KYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMX0NCm1vZGVsX2RmX3RvZ2V0aGVyICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZmRwLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoYWVzKGxpbmV0eXBlID0gbW9kZWwsIHNpemUgPSBtb2RlbCkpICsNCiAgICAgIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMobXNxcm9iID0gMS4xNSwgTVNzdGF0cyA9IDAuOCkpICsNCiAgICAgIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKG1zcXJvYiA9ICJzb2xpZCIsIE1Tc3RhdHMgPSAiZG90dGVkIikpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLjA1LGx0eT0yKSArDQogICAgICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9kYXRhX3RvZ2V0aGVyLCANCiAgICAgICAgICAgICAgICAgYWVzKHggPSB4LCB5ID0geSwgc2hhcGUgPSBtb2RlbCwgY29sb3IgPSByZXBzKSwgc2l6ZSA9IDIuNSkgKw0KICAgICAgZmFjZXRfZ3JpZCh2YXJzKGNvbmRpdGlvbnMpLCB2YXJzKHNkKSwgbGFiZWxsZXIgPSBsYWJlbF9ib3RoKSArDQogICAgICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTEpLA0KICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNSksDQogICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwNCiAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLCANCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTEpKQ0KYGBgDQoNClJPQyBjdXJ2ZXMNCg0KYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMX0NCm1vZGVsX2RmX3RvZ2V0aGVyICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZnByLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoYWVzKGxpbmV0eXBlID0gbW9kZWwsIHNpemUgPSBtb2RlbCkpICsNCiAgICAgIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMobXNxcm9iID0gMS4xLCBNU3N0YXRzID0gMS4xKSkgKw0KICAgICAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMobXNxcm9iID0gInNvbGlkIiwgTVNzdGF0cyA9ICJkb3R0ZWQiKSkgKw0KICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAuMDUsbHR5PTIpICsNCiAgICAgICNnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9kYXRhX3RvZ2V0aGVyLCBhZXMoeCA9IGZwciwgeSA9IHksIHNoYXBlID0gbW9kZWwpLCBzaXplID0gMiwgY29sb3IgPSAiZ3JheTMwIikgKw0KICAgICAgZmFjZXRfZ3JpZCh2YXJzKGNvbmRpdGlvbnMpLCB2YXJzKHNkKSwgbGFiZWxsZXIgPSBsYWJlbF9ib3RoKSArDQogICAgICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTEpLA0KICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNSksDQogICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwNCiAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLCANCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTEpKQ0KYGBgDQoNCiMjIyMgU2ltdWxhdGlvbiAyDQoNCmBgYHtyfQ0KbW9kZWxfZGYyIDwtIG1vZGVsX2RmMiAlPiUNCiAgICAgICAgICAgICAgZHBseXI6OnJlbmFtZShsb2dGQyA9IGxvZzJGQywNCiAgICAgICAgICAgICAgICAgICAgIHNlID0gU0UsDQogICAgICAgICAgICAgICAgICAgICB0ID0gVHZhbHVlLA0KICAgICAgICAgICAgICAgICAgICAgZGYgPSBERiwNCiAgICAgICAgICAgICAgICAgICAgIHB2YWwgPSBwdmFsdWUsDQogICAgICAgICAgICAgICAgICAgICBhZGpQdmFsID0gYWRqLnB2YWx1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUoZGF0YXNldCA9IHBhc3RlKHNkLCByZXBzLCBjb25kaXRpb25zLCBzZXAgPSAiXyIpKSAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KC1HbG9iYWxQcm90ZWluKQ0KDQptb2RlbF9kZjJfdG9nZXRoZXIgPC0gcmJpbmQobW9kZWxfZGYyLCBtc3Fyb2Jfc2ltdWxhdGlvbjIpDQpgYGANCg0KYGBge3J9DQpwb2ludF9kYXRhX3RvZ2V0aGVyXzIgPC0gcmJpbmQocG9pbnRfZGF0YV8yLCBwb2ludF9kYXRhXzQpDQpgYGANCg0KDQpgYGB7cn0NCnBvaW50X2RhdGFfc2ltMl9tc3N0YXRzIDwtIGxhcHBseShzZXEoMToyNCksIGZ1bmN0aW9uKHgpIHsNCiAgcDEgPSBnZXRfcG9pbnRfZGF0YShhZGp1c3RlZF9tb2RlbHNfc2ltMltbeF1dLCAiYWRqLnB2YWx1ZSIsIDAuMDEsICJNU3N0YXRzIikNCiAgcDUgPSBnZXRfcG9pbnRfZGF0YShhZGp1c3RlZF9tb2RlbHNfc2ltMltbeF1dLCAiYWRqLnB2YWx1ZSIsIDAuMDUsICJNU3N0YXRzIikNCiAgcDEwID0gZ2V0X3BvaW50X2RhdGEoYWRqdXN0ZWRfbW9kZWxzX3NpbTJbW3hdXSwgImFkai5wdmFsdWUiLCAwLjEsICJNU3N0YXRzIikNCiAgcmV0dXJuKHJiaW5kKHAxLCBwNSwgcDEwKSkNCn0pDQpwb2ludF9kYXRhX3NpbTJfbXNzdGF0cyA9IGRvLmNhbGwoInJiaW5kIiwgcG9pbnRfZGF0YV9zaW0yX21zc3RhdHMpDQoNCnBvaW50X2RhdGFfc2ltMl9tc3Fyb2IgPC0gbGFwcGx5KHVuaXF1ZShtc3Fyb2Jfc2ltdWxhdGlvbjIkZGF0YXNldCksIGZ1bmN0aW9uKHgpIHsNCiAgcDEgPSBnZXRfcG9pbnRfZGF0YShtc3Fyb2Jfc2ltdWxhdGlvbjJbbXNxcm9iX3NpbXVsYXRpb24yJGRhdGFzZXQ9PXgsXSwgImFkalB2YWwiLCAwLjAxLCAibXNxcm9iIikNCiAgcDUgPSBnZXRfcG9pbnRfZGF0YShtc3Fyb2Jfc2ltdWxhdGlvbjJbbXNxcm9iX3NpbXVsYXRpb24yJGRhdGFzZXQ9PXgsXSwgImFkalB2YWwiLCAwLjA1LCAibXNxcm9iIikNCiAgcDEwID0gZ2V0X3BvaW50X2RhdGEobXNxcm9iX3NpbXVsYXRpb24yW21zcXJvYl9zaW11bGF0aW9uMiRkYXRhc2V0PT14LF0sICJhZGpQdmFsIiwgMC4xLCAibXNxcm9iIikNCiAgcmV0dXJuKHJiaW5kKHAxLCBwNSwgcDEwKSkNCn0pDQpwb2ludF9kYXRhX3NpbTJfbXNxcm9iID0gZG8uY2FsbCgicmJpbmQiLCBwb2ludF9kYXRhX3NpbTJfbXNxcm9iKQ0KcG9pbnRfZGF0YV9zaW0yX3RvZ2V0aGVyID0gcmJpbmQocG9pbnRfZGF0YV9zaW0yX21zc3RhdHMsIHBvaW50X2RhdGFfc2ltMl9tc3Fyb2IpDQpwb2ludF9kYXRhX3NpbTJfdG9nZXRoZXIkYWxwaGEgPC0gYXMuZmFjdG9yKHBvaW50X2RhdGFfc2ltMl90b2dldGhlciRhbHBoYSkNCmBgYA0KDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTN9DQptb2RlbF9kZjJfdG9nZXRoZXIgJT4lDQogICAgICBnZ3Bsb3QoYWVzKHggPSBmZHAsIHkgPSB0cHIsIGNvbG9yID0gcmVwcykpICsNCiAgICAgIGdlb21fcGF0aChhZXMobGluZXR5cGUgPSBtb2RlbCksIHNpemUgPSAxKSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wNSxsdHk9MikgKw0KICAgICAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfZGF0YV9zaW0yX3RvZ2V0aGVyLCBhZXMoeCA9IHgsIHkgPSB5LCBzaGFwZSA9IGFscGhhLCBmaWxsID0gcmVwcyksIHNpemUgPSAxLjUsIGNvbG9yID0gImJsYWNrIikgKw0KICAgICAgZmFjZXRfZ3JpZCh2YXJzKHNkKSwgdmFycyhjb25kaXRpb25zKSwgbGFiZWxsZXIgPSBsYWJlbF9ib3RoKQ0KYGBgDQoNCg0KYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMX0NCm1vZGVsX2RmMl90b2dldGhlciAlPiUNCiAgICAgIGdncGxvdChhZXMoeCA9IGZkcCwgeSA9IHRwciwgY29sb3IgPSByZXBzKSkgKw0KICAgICAgZ2VvbV9wYXRoKGFlcyhsaW5ldHlwZSA9IG1vZGVsLCBzaXplID0gbW9kZWwpKSArDQogICAgICBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXMgPSBjKG1zcXJvYiA9IDEuMTUsIE1Tc3RhdHMgPSAwLjgpKSArDQogICAgICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYyhtc3Fyb2IgPSAic29saWQiLCBNU3N0YXRzID0gImRvdHRlZCIpKSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wNSxsdHk9MikgKw0KICAgICAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfZGF0YV90b2dldGhlcl8yLCBhZXMoeCA9IHgsIHkgPSB5LCBzaGFwZSA9IG1vZGVsLCBjb2xvciA9IHJlcHMpLCBzaXplID0gMi41KSArDQogICAgICBmYWNldF9ncmlkKHZhcnMoY29uZGl0aW9ucyksIHZhcnMoc2QpLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsNCiAgICAgIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE1KSwNCiAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksDQogICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksIA0KICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSkpDQpgYGANCg0KUk9DIGN1cnZlcw0KDQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTExfQ0KbW9kZWxfZGYyX3RvZ2V0aGVyICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZnByLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoYWVzKGxpbmV0eXBlID0gbW9kZWwsIHNpemUgPSBtb2RlbCkpICsNCiAgICAgIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMobXNxcm9iID0gMS4xLCBNU3N0YXRzID0gMS4xKSkgKw0KICAgICAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMobXNxcm9iID0gInNvbGlkIiwgTVNzdGF0cyA9ICJkb3R0ZWQiKSkgKw0KICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAuMDUsbHR5PTIpICsNCiAgICAgICNnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9kYXRhX3RvZ2V0aGVyXzIsIGFlcyh4ID0gZnByLCB5ID0geSwgc2hhcGUgPSBtb2RlbCksIHNpemUgPSAyLCBjb2xvciA9ICJncmF5MzUiKSArDQogICAgICBmYWNldF9ncmlkKHZhcnMoY29uZGl0aW9ucyksIHZhcnMoc2QpLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsNCiAgICAgIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE1KSwNCiAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksDQogICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksIA0KICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSkpDQpgYGANCg0KDQoNCiMjIHAgdmFsdWUgZGlzdHJpYnV0aW9ucw0KDQojIyMgbXNzdGF0cw0KDQojIyMjIFNpbXVsYXRpb24gMQ0KDQpgYGB7cn0NCnB2YWxzIDwtIGMoKQ0KZm9yIChpIGluIDE6bGVuZ3RoKGFkanVzdGVkX21vZGVsc19zaW0xKSl7DQogIHB2YWxzIDwtIGMocHZhbHMsIGRmICU+JQ0KICAgIGZpbHRlcihjaGFuZ2UgPT0gRikgJT4lDQogICAgcHVsbChwdmFsKSkNCg0KfQ0KaGlzdChwdmFscykNCmBgYA0KDQpgYGB7cn0NCnB2YWxzIDwtIGMoKQ0KZm9yIChpIGluIDE6bGVuZ3RoKGFkanVzdGVkX21vZGVsc19zaW0xKSl7DQogIHB2YWxzIDwtIGMocHZhbHMsIGRmICU+JQ0KICAgIGZpbHRlcihjaGFuZ2UgPT0gVCkgJT4lDQogICAgcHVsbChwdmFsKSkNCg0KfQ0KaGlzdChwdmFscykNCmBgYA0KDQoNCiMjIyBtc3Fyb2INCg0KIyMjIyBTaW11bGF0aW9uIDENCg0KYGBge3J9DQpwdmFscyA8LSBjKCkNCmZvciAoaSBpbiB1bmlxdWUobXNxcm9iX3NpbXVsYXRpb24xJGRhdGFzZXQpKXsNCiAgZGYgPC0gbXNxcm9iX3NpbXVsYXRpb24xW21zcXJvYl9zaW11bGF0aW9uMSRkYXRhc2V0PT1pLF0NCiAgcHZhbHMgPC0gYyhwdmFscywgZGYgJT4lDQogICAgZmlsdGVyKGdyZXBsKHBhdHRlcm4gPSAiTm9DaGFuZ2UiLCBQcm90ZWluKSkgJT4lDQogICAgICBwdWxsKHB2YWwpKQ0KfQ0KDQpoaXN0KHB2YWxzKQ0KYGBgDQoNCmBgYHtyfQ0KcHZhbHMgPC0gYygpDQpmb3IgKGkgaW4gdW5pcXVlKG1zcXJvYl9zaW11bGF0aW9uMSRkYXRhc2V0KSl7DQogIGRmIDwtIG1zcXJvYl9zaW11bGF0aW9uMVttc3Fyb2Jfc2ltdWxhdGlvbjEkZGF0YXNldD09aSxdDQogIHB2YWxzIDwtIGMocHZhbHMsIGRmICU+JQ0KICAgIGZpbHRlcighZ3JlcGwocGF0dGVybiA9ICJOb0NoYW5nZSIsIFByb3RlaW4pKSAlPiUNCiAgICAgIHB1bGwocHZhbCkpDQp9DQoNCmhpc3QocHZhbHMpDQpgYGANCg0KDQojIyMjIFNpbXVsYXRpb24gMg0KDQpgYGB7cn0NCnB2YWxzIDwtIGMoKQ0KZm9yIChpIGluIHVuaXF1ZShtc3Fyb2Jfc2ltdWxhdGlvbjIkZGF0YXNldCkpew0KICBkZiA8LSBtc3Fyb2Jfc2ltdWxhdGlvbjJbbXNxcm9iX3NpbXVsYXRpb24yJGRhdGFzZXQ9PWksXQ0KICBwdmFscyA8LSBjKHB2YWxzLCBkZiAlPiUNCiAgICBmaWx0ZXIoZ3JlcGwocGF0dGVybiA9ICJOb0NoYW5nZSIsIFByb3RlaW4pKSAlPiUNCiAgICAgIHB1bGwocHZhbCkpDQp9DQoNCmhpc3QocHZhbHMpDQpgYGANCg0KYGBge3J9DQpwdmFscyA8LSBjKCkNCmZvciAoaSBpbiB1bmlxdWUobXNxcm9iX3NpbXVsYXRpb24yJGRhdGFzZXQpKXsNCiAgZGYgPC0gbXNxcm9iX3NpbXVsYXRpb24yW21zcXJvYl9zaW11bGF0aW9uMiRkYXRhc2V0PT1pLF0NCiAgcHZhbHMgPC0gYyhwdmFscywgZGYgJT4lDQogICAgZmlsdGVyKCFncmVwbChwYXR0ZXJuID0gIk5vQ2hhbmdlIiwgUHJvdGVpbikpICU+JQ0KICAgICAgcHVsbChwdmFsKSkNCn0NCg0KaGlzdChwdmFscykNCmBgYA0KDQoNCg0KIyMjIFZlcnN1cyB3aGVuIHN0YXJ0aW5nIGZyb20gc3VtbWFyaXNlZCBkYXRhDQoNCmBgYHtyfQ0KbG9hZCgiQzovVXNlcnMvTmluYS9PbmVEcml2ZSAtIFVHZW50L0RvY3VtZW50ZW4vRG9jdG9yYWF0L21zcXJvYlBUTS9EYXRhc2V0cyBNU1N0YXRzL3NpbXVsYXRpb25fZGF0YS9zaWdudGFibGVfc2ltdWxhdGlvbjFfbXNxcm9iX2NvbXBhcmlzb24xX3N0YXJ0ZnJvbXN1bW1hcnkucmRhIikNCnNpZ25fdGFibGUxMSA8LSBzaWduX3RhYmxlDQpsb2FkKCJDOi9Vc2Vycy9OaW5hL09uZURyaXZlIC0gVUdlbnQvRG9jdW1lbnRlbi9Eb2N0b3JhYXQvbXNxcm9iUFRNL0RhdGFzZXRzIE1TU3RhdHMvc2ltdWxhdGlvbl9kYXRhL3NpZ250YWJsZV9zaW11bGF0aW9uMV9tc3Fyb2JfY29tcGFyaXNvbjJfc3RhcnRmcm9tc3VtbWFyeS5yZGEiKQ0Kc2lnbl90YWJsZTEyIDwtIHNpZ25fdGFibGUNCmxvYWQoIkM6L1VzZXJzL05pbmEvT25lRHJpdmUgLSBVR2VudC9Eb2N1bWVudGVuL0RvY3RvcmFhdC9tc3Fyb2JQVE0vRGF0YXNldHMgTVNTdGF0cy9zaW11bGF0aW9uX2RhdGEvc2lnbnRhYmxlX3NpbXVsYXRpb24xX21zcXJvYl9jb21wYXJpc29uM19zdGFydGZyb21zdW1tYXJ5LnJkYSIpDQpzaWduX3RhYmxlMTMgPC0gc2lnbl90YWJsZQ0KDQpsb2FkKCJDOi9Vc2Vycy9OaW5hL09uZURyaXZlIC0gVUdlbnQvRG9jdW1lbnRlbi9Eb2N0b3JhYXQvbXNxcm9iUFRNL0RhdGFzZXRzIE1TU3RhdHMvc2ltdWxhdGlvbl9kYXRhL3NpZ250YWJsZV9zaW11bGF0aW9uMl9tc3Fyb2JfY29tcGFyaXNvbjFfc3RhcnRmcm9tc3VtbWFyeS5yZGEiKQ0Kc2lnbl90YWJsZTIxIDwtIHNpZ25fdGFibGUNCmxvYWQoIkM6L1VzZXJzL05pbmEvT25lRHJpdmUgLSBVR2VudC9Eb2N1bWVudGVuL0RvY3RvcmFhdC9tc3Fyb2JQVE0vRGF0YXNldHMgTVNTdGF0cy9zaW11bGF0aW9uX2RhdGEvc2lnbnRhYmxlX3NpbXVsYXRpb24yX21zcXJvYl9jb21wYXJpc29uMl9zdGFydGZyb21zdW1tYXJ5LnJkYSIpDQpzaWduX3RhYmxlMjIgPC0gc2lnbl90YWJsZQ0KbG9hZCgiQzovVXNlcnMvTmluYS9PbmVEcml2ZSAtIFVHZW50L0RvY3VtZW50ZW4vRG9jdG9yYWF0L21zcXJvYlBUTS9EYXRhc2V0cyBNU1N0YXRzL3NpbXVsYXRpb25fZGF0YS9zaWdudGFibGVfc2ltdWxhdGlvbjJfbXNxcm9iX2NvbXBhcmlzb24zX3N0YXJ0ZnJvbXN1bW1hcnkucmRhIikNCnNpZ25fdGFibGUyMyA8LSBzaWduX3RhYmxlDQpgYGANCg0KYGBge3J9DQptc3Fyb2Jfc2ltdWxhdGlvbjFfc3VtbWFyeSA8LSByYmluZChzaWduX3RhYmxlMTEsIHNpZ25fdGFibGUxMiwgc2lnbl90YWJsZTEzKQ0KbXNxcm9iX3NpbXVsYXRpb24yX3N1bW1hcnkgPC0gcmJpbmQoc2lnbl90YWJsZTIxLCBzaWduX3RhYmxlMjIsIHNpZ25fdGFibGUyMykNCmBgYA0KDQojIyMjIFNpbXVsYXRpb24gMQ0KDQpgYGB7cn0NCnBvaW50X2RhdGFfNyA8LSB0aWJibGUoKQ0KZm9yIChpIGluIHVuaXF1ZShtc3Fyb2Jfc2ltdWxhdGlvbjFfc3VtbWFyeSRkYXRhc2V0KSl7DQogIGRmIDwtIG1zcXJvYl9zaW11bGF0aW9uMV9zdW1tYXJ5W21zcXJvYl9zaW11bGF0aW9uMV9zdW1tYXJ5JGRhdGFzZXQ9PWksXQ0KICByb3dfID0gc3VtKGRmJGFkalB2YWwgPCAwLjA1LCBuYS5ybSA9VCkNCiAgaWYgKHJvd18gPT0gMCl7DQogICAgcG9pbnQgPSB0aWJibGUoeCA9IDAsIHkgPSAwLCBmcHIgPSAwLA0KICAgICAgICAgICAgICAgICAgIHNkID0gYXMuZmFjdG9yKGRmWzEsXSRzZCksIA0KICAgICAgICAgICAgICAgICAgIHJlcHMgPSBhcy5mYWN0b3IoZGZbMSxdJHJlcHMpLCANCiAgICAgICAgICAgICAgICAgICBjb25kaXRpb25zID0gYXMuZmFjdG9yKGRmWzEsXSRjb25kaXRpb25zKSkNCiAgfQ0KICBlbHNlIHsNCiAgeCA9IGRmW3Jvd18sXSRmZHANCiAgZnByID0gZGZbcm93XyxdJGZwcg0KICB5ID0gZGZbcm93XyxdJHRwcg0KICBzZCA9IGFzLmZhY3RvcihkZltyb3dfLF0kc2QpDQogIHJlcHMgPSBhcy5mYWN0b3IoZGZbcm93XyxdJHJlcHMpDQogIGNvbmRpdGlvbnMgPSBhcy5mYWN0b3IoZGZbcm93XyxdJGNvbmRpdGlvbnMpDQogIHBvaW50ID0gdGliYmxlKHggPSB4LCB5ID0geSwgZnByID0gZnByLCBzZCA9IHNkLCByZXBzID0gcmVwcywgY29uZGl0aW9ucyA9IGNvbmRpdGlvbnMsIG1vZGVsID0gIm1zcXJvYl9zdW1tYXJ5U3RhcnQiKQ0KICB9DQogIHBvaW50X2RhdGFfNyA9IHJiaW5kKHBvaW50X2RhdGFfNywgcG9pbnQpDQp9DQpgYGANCg0KYGBge3J9DQptc3Fyb2Jfc2ltdWxhdGlvbjFfc3VtbWFyeSA8LSANCiAgbXNxcm9iX3NpbXVsYXRpb24xX3N1bW1hcnkgJT4lICAgICANCiAgcm93bmFtZXNfdG9fY29sdW1uKCJQcm90ZWluIikgJT4lDQogIG11dGF0ZShjaGFuZ2UgPSAhZ3JlcGwoIk5vQ2hhbmdlIiwgUHJvdGVpbiksDQogICAgICAgICBkYXRhc2V0ID0gcGFzdGUoc2QsIHJlcHMsIGNvbmRpdGlvbnMsIHNlcCA9ICJfIiksDQogICAgICAgICBtb2RlbCA9ICJtc3Fyb2Jfc3VtbWFyeVN0YXJ0Ig0KKQ0KYGBgDQoNCmBgYHtyfQ0KbXNxcm9iX3NpbXVsYXRpb24xX3N1bW1hcnkkdHByIDwtIDEwMDANCm1zcXJvYl9zaW11bGF0aW9uMV9zdW1tYXJ5JGZkcCA8LSAxMDAwDQptc3Fyb2Jfc2ltdWxhdGlvbjFfc3VtbWFyeSRmcHIgPC0gMTAwMA0KZm9yIChpIGluIHVuaXF1ZShtc3Fyb2Jfc2ltdWxhdGlvbjFfc3VtbWFyeSRkYXRhc2V0KSl7DQogIG1zcXJvYl9zaW11bGF0aW9uMV9zdW1tYXJ5W21zcXJvYl9zaW11bGF0aW9uMV9zdW1tYXJ5JGRhdGFzZXQ9PWksXSA8LSANCiAgICB0cHJGZHAobXNxcm9iX3NpbXVsYXRpb24xX3N1bW1hcnlbbXNxcm9iX3NpbXVsYXRpb24xX3N1bW1hcnkkZGF0YXNldD09aSxdLCAicHZhbCIsICJjaGFuZ2UiKQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQpzaW11bGF0aW9uMV90b2dldGhlciA8LSByYmluZChtc3Fyb2Jfc2ltdWxhdGlvbjFfc3VtbWFyeSwgbXNxcm9iX3NpbXVsYXRpb24xKQ0KYGBgDQoNCmBgYHtyfQ0KcG9pbnRfZGF0YV90b2dldGhlciA8LSByYmluZChwb2ludF9kYXRhXzMsIHBvaW50X2RhdGFfNykNCmBgYA0KDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTN9DQpzaW11bGF0aW9uMV90b2dldGhlciAlPiUNCiAgICAgIGdncGxvdChhZXMoeCA9IGZkcCwgeSA9IHRwciwgY29sb3IgPSByZXBzKSkgKw0KICAgICAgZ2VvbV9wYXRoKGFlcyhsaW5ldHlwZSA9IG1vZGVsKSwgc2l6ZSA9IDEpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLjA1LGx0eT0yKSArDQogICAgICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9kYXRhX3RvZ2V0aGVyLCBhZXMoeCA9IHgsIHkgPSB5LCBzaGFwZSA9IG1vZGVsKSwgc2l6ZSA9IDEuNSwgY29sb3IgPSAiYmxhY2siKSArDQogICAgICBmYWNldF9ncmlkKHZhcnMoc2QpLCB2YXJzKGNvbmRpdGlvbnMpLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpDQpgYGANCg0KYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMX0NCnNpbXVsYXRpb24xX3RvZ2V0aGVyICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZnByLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoYWVzKGxpbmV0eXBlID0gbW9kZWwsIHNpemUgPSBtb2RlbCkpICsNCiAgICAgIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMobXNxcm9iID0gMS4xLCBtc3Fyb2Jfc3VtbWFyeVN0YXJ0ID0gMS4xKSkgKw0KICAgICAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMobXNxcm9iID0gInNvbGlkIiwgbXNxcm9iX3N1bW1hcnlTdGFydCA9ICJkb3R0ZWQiKSkgKw0KICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAuMDUsbHR5PTIpICsNCiAgICAgICNnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9kYXRhX3RvZ2V0aGVyXzIsIGFlcyh4ID0gZnByLCB5ID0geSwgc2hhcGUgPSBtb2RlbCksIHNpemUgPSAyLCBjb2xvciA9ICJncmF5MzUiKSArDQogICAgICBmYWNldF9ncmlkKHZhcnMoY29uZGl0aW9ucyksIHZhcnMoc2QpLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsNCiAgICAgIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE1KSwNCiAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksDQogICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksIA0KICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSkpDQpgYGANCg0KDQoNCiMjIyMgU2ltdWxhdGlvbiAyDQoNCmBgYHtyfQ0KcG9pbnRfZGF0YV84IDwtIHRpYmJsZSgpDQpmb3IgKGkgaW4gdW5pcXVlKG1zcXJvYl9zaW11bGF0aW9uMl9zdW1tYXJ5JGRhdGFzZXQpKXsNCiAgZGYgPC0gbXNxcm9iX3NpbXVsYXRpb24yX3N1bW1hcnlbbXNxcm9iX3NpbXVsYXRpb24yX3N1bW1hcnkkZGF0YXNldD09aSxdDQogIHJvd18gPSBzdW0oZGYkYWRqUHZhbCA8IDAuMDUsIG5hLnJtID1UKQ0KICBpZiAocm93XyA9PSAwKXsNCiAgICBwb2ludCA9IHRpYmJsZSh4ID0gMCwgeSA9IDAsIGZwciA9IDAsDQogICAgICAgICAgICAgICAgICAgc2QgPSBhcy5mYWN0b3IoZGZbMSxdJHNkKSwgDQogICAgICAgICAgICAgICAgICAgcmVwcyA9IGFzLmZhY3RvcihkZlsxLF0kcmVwcyksIA0KICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbnMgPSBhcy5mYWN0b3IoZGZbMSxdJGNvbmRpdGlvbnMpKQ0KICB9DQogIGVsc2Ugew0KICB4ID0gZGZbcm93XyxdJGZkcA0KICBmcHIgPSBkZltyb3dfLF0kZnByDQogIHkgPSBkZltyb3dfLF0kdHByDQogIHNkID0gYXMuZmFjdG9yKGRmW3Jvd18sXSRzZCkNCiAgcmVwcyA9IGFzLmZhY3RvcihkZltyb3dfLF0kcmVwcykNCiAgY29uZGl0aW9ucyA9IGFzLmZhY3RvcihkZltyb3dfLF0kY29uZGl0aW9ucykNCiAgcG9pbnQgPSB0aWJibGUoeCA9IHgsIHkgPSB5LCBmcHIgPSBmcHIsIHNkID0gc2QsIHJlcHMgPSByZXBzLCBjb25kaXRpb25zID0gY29uZGl0aW9ucywgbW9kZWwgPSAibXNxcm9iX3N1bW1hcnlTdGFydCIpDQogIH0NCiAgcG9pbnRfZGF0YV84ID0gcmJpbmQocG9pbnRfZGF0YV84LCBwb2ludCkNCn0NCmBgYA0KDQpgYGB7cn0NCm1zcXJvYl9zaW11bGF0aW9uMl9zdW1tYXJ5IDwtIA0KICBtc3Fyb2Jfc2ltdWxhdGlvbjJfc3VtbWFyeSAlPiUgICAgIA0KICByb3duYW1lc190b19jb2x1bW4oIlByb3RlaW4iKSAlPiUNCiAgbXV0YXRlKGNoYW5nZSA9ICFncmVwbCgiTm9DaGFuZ2UiLCBQcm90ZWluKSwNCiAgICAgICAgIGRhdGFzZXQgPSBwYXN0ZShzZCwgcmVwcywgY29uZGl0aW9ucywgc2VwID0gIl8iKSwNCiAgICAgICAgIG1vZGVsID0gIm1zcXJvYl9zdW1tYXJ5U3RhcnQiDQopDQpgYGANCg0KYGBge3J9DQptc3Fyb2Jfc2ltdWxhdGlvbjJfc3VtbWFyeSR0cHIgPC0gMTAwMA0KbXNxcm9iX3NpbXVsYXRpb24yX3N1bW1hcnkkZmRwIDwtIDEwMDANCm1zcXJvYl9zaW11bGF0aW9uMl9zdW1tYXJ5JGZwciA8LSAxMDAwDQpmb3IgKGkgaW4gdW5pcXVlKG1zcXJvYl9zaW11bGF0aW9uMl9zdW1tYXJ5JGRhdGFzZXQpKXsNCiAgbXNxcm9iX3NpbXVsYXRpb24yX3N1bW1hcnlbbXNxcm9iX3NpbXVsYXRpb24yX3N1bW1hcnkkZGF0YXNldD09aSxdIDwtIA0KICAgIHRwckZkcChtc3Fyb2Jfc2ltdWxhdGlvbjJfc3VtbWFyeVttc3Fyb2Jfc2ltdWxhdGlvbjJfc3VtbWFyeSRkYXRhc2V0PT1pLF0sICJwdmFsIiwgImNoYW5nZSIpDQp9DQpgYGANCg0KDQpgYGB7cn0NCnNpbXVsYXRpb24yX3RvZ2V0aGVyIDwtIHJiaW5kKG1zcXJvYl9zaW11bGF0aW9uMl9zdW1tYXJ5LCBtc3Fyb2Jfc2ltdWxhdGlvbjIpDQpgYGANCg0KYGBge3J9DQpwb2ludF9kYXRhX3RvZ2V0aGVyIDwtIHJiaW5kKHBvaW50X2RhdGFfNCwgcG9pbnRfZGF0YV84KQ0KYGBgDQoNCg0KYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xM30NCnNpbXVsYXRpb24yX3RvZ2V0aGVyICU+JQ0KICAgICAgZ2dwbG90KGFlcyh4ID0gZmRwLCB5ID0gdHByLCBjb2xvciA9IHJlcHMpKSArDQogICAgICBnZW9tX3BhdGgoYWVzKGxpbmV0eXBlID0gbW9kZWwpLCBzaXplID0gMSkgKw0KICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAuMDUsbHR5PTIpICsNCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHBvaW50X2RhdGFfdG9nZXRoZXIsIGFlcyh4ID0geCwgeSA9IHksIHNoYXBlID0gbW9kZWwpLCBzaXplID0gMS41LCBjb2xvciA9ICJibGFjayIpICsNCiAgICAgIGZhY2V0X2dyaWQodmFycyhzZCksIHZhcnMoY29uZGl0aW9ucyksIGxhYmVsbGVyID0gbGFiZWxfYm90aCkNCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTExfQ0Kc2ltdWxhdGlvbjJfdG9nZXRoZXIgJT4lDQogICAgICBnZ3Bsb3QoYWVzKHggPSBmcHIsIHkgPSB0cHIsIGNvbG9yID0gcmVwcykpICsNCiAgICAgIGdlb21fcGF0aChhZXMobGluZXR5cGUgPSBtb2RlbCwgc2l6ZSA9IG1vZGVsKSkgKw0KICAgICAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYyhtc3Fyb2IgPSAxLjEsIG1zcXJvYl9zdW1tYXJ5U3RhcnQgPSAxLjEpKSArDQogICAgICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYyhtc3Fyb2IgPSAic29saWQiLCBtc3Fyb2Jfc3VtbWFyeVN0YXJ0ID0gImRvdHRlZCIpKSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MC4wNSxsdHk9MikgKw0KICAgICAgI2dlb21fcG9pbnQoZGF0YSA9IHBvaW50X2RhdGFfdG9nZXRoZXJfMiwgYWVzKHggPSBmcHIsIHkgPSB5LCBzaGFwZSA9IG1vZGVsKSwgc2l6ZSA9IDIsIGNvbG9yID0gImdyYXkzNSIpICsNCiAgICAgIGZhY2V0X2dyaWQodmFycyhjb25kaXRpb25zKSwgdmFycyhzZCksIGxhYmVsbGVyID0gbGFiZWxfYm90aCkgKw0KICAgICAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTExKSwNCiAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTUpLA0KICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksDQogICAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwNCiAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwgDQogICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTExKSkNCmBgYA==